SLING-7205 - Generate import statements for data-sly-use Java use objects
* added support for generating the import statements for fully qualified Java
class names passed as identifiers to data-sly-resource; use objects stored in the
repository are ignored
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1812544 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index b891e9d..51b6c0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
<properties>
<jacoco.maven.plugin.version>0.7.9</jacoco.maven.plugin.version>
+ <sling.java.version>8</sling.java.version>
</properties>
<!-- ======================================================================= -->
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/JavaClassBackendCompiler.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/JavaClassBackendCompiler.java
index 87e9655..8069cb0 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/JavaClassBackendCompiler.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/JavaClassBackendCompiler.java
@@ -38,7 +38,8 @@
private static final String mainTemplate;
private static final String childTemplate;
- private UnitBuilder unitBuilder = new UnitBuilder();
+ private UnitBuilder unitBuilder;
+ private final JavaImportsAnalyzer JAVA_IMPORTS_ANALYZER;
static {
try {
@@ -50,6 +51,14 @@
}
}
+ public JavaClassBackendCompiler() {
+ this(new JavaImportsAnalyzer() {});
+ }
+
+ public JavaClassBackendCompiler(JavaImportsAnalyzer javaImportsAnalyzer) {
+ JAVA_IMPORTS_ANALYZER = javaImportsAnalyzer;
+ unitBuilder = new UnitBuilder(JAVA_IMPORTS_ANALYZER);
+ }
@Override
public void handle(CommandStream stream) {
@@ -76,6 +85,13 @@
JavaClassTemplate mainTemplate = newMainTemplate();
mainTemplate.setPackageName(classInfo.getPackageName());
mainTemplate.setClassName(classInfo.getSimpleClassName());
+ StringBuilder imports = new StringBuilder();
+ for (String importClass : unitBuilder.getImports()) {
+ if (JAVA_IMPORTS_ANALYZER.allowImport(importClass)) {
+ imports.append("import ").append(importClass).append(";").append(System.lineSeparator());
+ }
+ }
+ mainTemplate.setImports(imports.toString());
processCompilationResult(compilationOutput, mainTemplate);
return mainTemplate.toString();
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/JavaImportsAnalyzer.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/JavaImportsAnalyzer.java
new file mode 100644
index 0000000..23a94e4
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/JavaImportsAnalyzer.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.java.compiler;
+
+/**
+ * The {@code JavaImportsAnalyzer} allows checking imports in generated HTL Java classes, in order to optimise dependencies.
+ */
+public interface JavaImportsAnalyzer {
+
+ /**
+ * Analyses the provided {@code importedClass} and decides if this class should be an explicit import or not in the generated HTL
+ * Java class.
+ *
+ * @param importedClass the import to analyse
+ * @return {@code true} if the import should be declared, {@code false} otherwise
+ */
+ default boolean allowImport(String importedClass) {
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/CodeGenVisitor.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/CodeGenVisitor.java
index a619ff6..629f9ac 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/CodeGenVisitor.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/CodeGenVisitor.java
@@ -117,7 +117,7 @@
@Override
public void visit(VariableBinding.Start variableBinding) {
source.startBlock();
- TypeInfo typeInfo = TypeInference.inferTypes(variableBinding.getExpression(), analyzer);
+ TypeInfo typeInfo = TypeInference.inferTypes(variableBinding.getExpression(), analyzer, unitBuilder.getImports());
Type type = typeInfo.typeOf(variableBinding.getExpression());
String properName = declare(variableBinding.getVariableName(), type);
source.beginAssignment(properName, type.getNativeClass());
@@ -142,7 +142,7 @@
@Override
public void visit(VariableBinding.Global globalAssignment) {
- TypeInfo typeInfo = TypeInference.inferTypes(globalAssignment.getExpression(), analyzer);
+ TypeInfo typeInfo = TypeInference.inferTypes(globalAssignment.getExpression(), analyzer, unitBuilder.getImports());
VariableDescriptor descriptor = analyzer.declareGlobal(globalAssignment.getVariableName());
String name = descriptor.getAssignedName();
source.append(name).assign();
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/JavaClassTemplate.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/JavaClassTemplate.java
index 34be88b..85a0236 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/JavaClassTemplate.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/JavaClassTemplate.java
@@ -23,10 +23,10 @@
private String classTemplate;
-
- private static final String MAIN_BODY = "MainBody";
- private static final String CLASS_NAME = "ClassName";
private static final String PACKAGE_NAME = "PackageName";
+ private static final String IMPORTS = "Imports";
+ private static final String CLASS_NAME = "ClassName";
+ private static final String MAIN_BODY = "MainBody";
private static final String TEMPLATE_INIT = "SubTemplateMapInit";
private static final String NAME = "Name";
@@ -52,6 +52,10 @@
setPart(PACKAGE_NAME, name);
}
+ public void setImports(String imports) {
+ setPart(IMPORTS, imports);
+ }
+
@Override
public String toString() {
return insertPart(TEMPLATE_INIT, classTemplate, templateInitBuilder.toString());
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/Type.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/Type.java
index e73d5e5..187c650 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/Type.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/Type.java
@@ -20,27 +20,38 @@
/**
* Type inferred for an expression
*/
-public enum Type {
- UNKNOWN("Object", false),
- STRING("String", false),
- LONG("long", true),
- DOUBLE("double", true),
- BOOLEAN("boolean", true),
- MAP("java.util.Map", false);
+public class Type {
- private final String nativeClass;
+ public static final Type UNKNOWN = new Type("Object", false, null);
+ public static final Type STRING = new Type("String", false, null);
+ public static final Type LONG = new Type("long", true, 0);
+ public static final Type DOUBLE = new Type("double", true, 0.0d);
+ public static final Type BOOLEAN = new Type("boolean", true, false);
+ public static final Type MAP = new Type("java.util.Map", false, null);
+
+ private String nativeClass;
private final boolean isPrimitive;
+ private final Object defaultValue;
- Type(String nativeClass, boolean isPrimitive) {
+ private Type(String nativeClass, boolean isPrimitive, Object defaultValue) {
this.nativeClass = nativeClass;
this.isPrimitive = isPrimitive;
+ this.defaultValue = defaultValue;
}
public String getNativeClass() {
return nativeClass;
}
+ public Object getDefaultValue() {
+ return defaultValue;
+ }
+
public boolean isPrimitive() {
return isPrimitive;
}
+
+ public static Type dynamic(String type) {
+ return new Type(type, false, null);
+ }
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/TypeInference.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/TypeInference.java
index 39377b8..6511de3 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/TypeInference.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/TypeInference.java
@@ -19,10 +19,10 @@
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
-import org.apache.sling.scripting.sightly.java.compiler.impl.operator.BinaryOpGen;
-import org.apache.sling.scripting.sightly.java.compiler.impl.operator.Operators;
-import org.apache.sling.scripting.sightly.java.compiler.impl.operator.UnaryOpGen;
+import org.apache.sling.scripting.sightly.compiler.RuntimeFunction;
import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
import org.apache.sling.scripting.sightly.compiler.expression.NodeVisitor;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.ArrayLiteral;
@@ -37,6 +37,9 @@
import org.apache.sling.scripting.sightly.compiler.expression.nodes.StringConstant;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.TernaryOperator;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation;
+import org.apache.sling.scripting.sightly.java.compiler.impl.operator.BinaryOpGen;
+import org.apache.sling.scripting.sightly.java.compiler.impl.operator.Operators;
+import org.apache.sling.scripting.sightly.java.compiler.impl.operator.UnaryOpGen;
/**
* Expression translator which uses type information
@@ -44,17 +47,20 @@
public final class TypeInference implements NodeVisitor<Type> {
private final VariableAnalyzer analyzer;
- private final Map<ExpressionNode, Type> inferMap = new IdentityHashMap<ExpressionNode, Type>();
+ private final Map<ExpressionNode, Type> inferMap = new IdentityHashMap<>();
+ private final Set<String> imports;
+ private static final Pattern FQCN_PATTERN = Pattern.compile("([[\\p{L}&&[^\\p{Lu}]]_$][\\p{L}\\p{N}_$]*\\.)+[\\p{Lu}_$][\\p{L}\\p{N}_$]*");
- public static TypeInfo inferTypes(ExpressionNode node, VariableAnalyzer analyzer) {
- TypeInference typeInference = new TypeInference(analyzer);
+ public static TypeInfo inferTypes(ExpressionNode node, VariableAnalyzer analyzer, Set<String> imports) {
+ TypeInference typeInference = new TypeInference(analyzer, imports);
typeInference.infer(node);
return new TypeInfo(typeInference.inferMap);
}
- private TypeInference(VariableAnalyzer analyzer) {
+ private TypeInference(VariableAnalyzer analyzer, Set<String> imports) {
this.analyzer = analyzer;
+ this.imports = imports;
}
@@ -128,6 +134,16 @@
@Override
public Type evaluate(RuntimeCall runtimeCall) {
inferAll(runtimeCall.getArguments());
+ if (runtimeCall.getFunctionName().equals(RuntimeFunction.USE)) {
+ ExpressionNode identifier = runtimeCall.getArguments().get(0);
+ if (identifier instanceof StringConstant) {
+ String objectType = ((StringConstant) identifier).getText();
+ if (FQCN_PATTERN.matcher(objectType).matches()) {
+ imports.add(objectType);
+ return Type.dynamic(objectType);
+ }
+ }
+ }
return Type.UNKNOWN;
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/UnitBuilder.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/UnitBuilder.java
index 990bf8c..3a7678c 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/UnitBuilder.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/UnitBuilder.java
@@ -19,10 +19,12 @@
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.sling.scripting.sightly.java.compiler.CompilationOutput;
+import org.apache.sling.scripting.sightly.java.compiler.JavaImportsAnalyzer;
/**
* Builder for compiled sources
@@ -32,9 +34,12 @@
private final JavaSource source = new JavaSource();
private final Set<String> parameters;
private final Map<String, UnitBuilder> subTemplates = new HashMap<String, UnitBuilder>();
+ private Set<String> imports = new HashSet<>();
+ private JavaImportsAnalyzer javaImportsAnalyzer;
- public UnitBuilder() {
- this(Collections.<String>emptySet());
+ public UnitBuilder(JavaImportsAnalyzer javaImportsAnalyzer) {
+ this(Collections.emptySet());
+ this.javaImportsAnalyzer = javaImportsAnalyzer;
}
public UnitBuilder(Set<String> parameters) {
@@ -62,4 +67,12 @@
}
return new CompilationOutputImpl(source.toString(), map);
}
+
+ public JavaImportsAnalyzer getJavaImportsAnalyzer() {
+ return javaImportsAnalyzer;
+ }
+
+ public Set<String> getImports() {
+ return imports;
+ }
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/VariableDescriptor.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/VariableDescriptor.java
index 51193d6..800a5bd 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/VariableDescriptor.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/impl/VariableDescriptor.java
@@ -78,7 +78,7 @@
}
/**
- * Get the inferred type fot this variable
+ * Get the inferred type for this variable
* @return - the class of the variable
*/
public Type getType() {
diff --git a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/package-info.java
index 4502fd2..45ddcbe 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/java/compiler/package-info.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/java/compiler/package-info.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
-@Version("1.1.0")
+@Version("1.2.0")
package org.apache.sling.scripting.sightly.java.compiler;
import org.osgi.annotation.versioning.Version;
diff --git a/src/main/resources/templates/compiled_unit_template.txt b/src/main/resources/templates/compiled_unit_template.txt
index a327d6a..3ce6585 100644
--- a/src/main/resources/templates/compiled_unit_template.txt
+++ b/src/main/resources/templates/compiled_unit_template.txt
@@ -25,6 +25,8 @@
import org.apache.sling.scripting.sightly.java.compiler.RenderUnit;
import org.apache.sling.scripting.sightly.render.RenderContext;
+##Imports##
+
public final class ##ClassName## extends RenderUnit {
@Override
diff --git a/src/test/java/org/apache/sling/scripting/sightly/compiler/java/JavaClassBackendCompilerTest.java b/src/test/java/org/apache/sling/scripting/sightly/compiler/java/JavaClassBackendCompilerTest.java
index 3dd3c8c..924e519 100644
--- a/src/test/java/org/apache/sling/scripting/sightly/compiler/java/JavaClassBackendCompilerTest.java
+++ b/src/test/java/org/apache/sling/scripting/sightly/compiler/java/JavaClassBackendCompilerTest.java
@@ -93,6 +93,18 @@
assertEquals(expectedOutput, writer.toString());
}
+ @Test
+ public void generateImportStatements() throws Exception {
+ CompilationUnit compilationUnit = TestUtils.readScriptFromClasspath("/imports.html");
+ JavaClassBackendCompiler backendCompiler = new JavaClassBackendCompiler();
+ SightlyCompiler sightlyCompiler = new SightlyCompiler();
+ sightlyCompiler.compile(compilationUnit, backendCompiler);
+ ClassInfo classInfo = buildClassInfo("imports");
+ String source = backendCompiler.build(classInfo);
+ String expectedJavaOutput = IOUtils.toString(this.getClass().getResourceAsStream("/imports.html.java"), "UTF-8");
+ assertEquals(expectedJavaOutput, source);
+ }
+
private ClassInfo buildClassInfo(final String info) {
return new ClassInfo() {
@Override
diff --git a/src/test/resources/imports.html b/src/test/resources/imports.html
new file mode 100644
index 0000000..82d1905
--- /dev/null
+++ b/src/test/resources/imports.html
@@ -0,0 +1,22 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+<div data-sly-use.slingSettings="org.apache.sling.settings.SlingSettingsService"
+ data-sly-use.js="script.js"
+ data-sly-use.pojo="Pojo"
+></div>
diff --git a/src/test/resources/imports.html.java b/src/test/resources/imports.html.java
new file mode 100644
index 0000000..f80f7db
--- /dev/null
+++ b/src/test/resources/imports.html.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.compiler.java;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import javax.script.Bindings;
+
+import org.apache.sling.scripting.sightly.java.compiler.RenderUnit;
+import org.apache.sling.scripting.sightly.render.RenderContext;
+
+import org.apache.sling.settings.SlingSettingsService;
+
+
+public final class Test_imports extends RenderUnit {
+
+ @Override
+ protected final void render(PrintWriter out,
+ Bindings bindings,
+ Bindings arguments,
+ RenderContext renderContext) {
+// Main Template Body -----------------------------------------------------------------------------
+
+Object _global_slingsettings = null;
+Object _global_js = null;
+Object _global_pojo = null;
+out.write("\n");
+_global_slingsettings = renderContext.call("use", "org.apache.sling.settings.SlingSettingsService", obj());
+_global_js = renderContext.call("use", "script.js", obj());
+_global_pojo = renderContext.call("use", "Pojo", obj());
+out.write("<div></div>\n");
+
+
+// End Of Main Template Body ----------------------------------------------------------------------
+ }
+
+
+
+ {
+//Sub-Templates Initialization --------------------------------------------------------------------
+
+
+
+//End of Sub-Templates Initialization -------------------------------------------------------------
+ }
+
+}
+