You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2020/02/25 12:07:29 UTC

[sling-org-apache-sling-commons-compiler] branch master updated: SLING-9135 - Move org.apache.sling.scripting.sightly.java.compiler.JavaEscapeUtils into org.apache.sling.commons.compiler

This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-compiler.git


The following commit(s) were added to refs/heads/master by this push:
     new 0d8f738  SLING-9135 - Move org.apache.sling.scripting.sightly.java.compiler.JavaEscapeUtils into org.apache.sling.commons.compiler
0d8f738 is described below

commit 0d8f738e432aa92a3fd54eba8413c857a835f928
Author: Radu Cotescu <17...@users.noreply.github.com>
AuthorDate: Tue Feb 25 13:07:21 2020 +0100

    SLING-9135 - Move org.apache.sling.scripting.sightly.java.compiler.JavaEscapeUtils into org.apache.sling.commons.compiler
    
    * refactored org.apache.sling.scripting.sightly.java.compiler.JavaEscapeUtils and
    extracted it into org.apache.sling.commons.compiler.source.JavaEscapeHelper
---
 pom.xml                                            |   6 +
 .../commons/compiler/source/JavaEscapeHelper.java  | 220 +++++++++++++++++++++
 .../commons/compiler/source/package-info.java      |  22 +++
 .../compiler/source/JavaEscapeHelperTest.java      |  59 ++++++
 4 files changed, 307 insertions(+)

diff --git a/pom.xml b/pom.xml
index 732451b..64b1391 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,6 +99,12 @@
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>16.0.2</version>
+            <scope>provided</scope>
+        </dependency>
 
         <!-- testing -->
         <dependency>
diff --git a/src/main/java/org/apache/sling/commons/compiler/source/JavaEscapeHelper.java b/src/main/java/org/apache/sling/commons/compiler/source/JavaEscapeHelper.java
new file mode 100644
index 0000000..cf7e72c
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/source/JavaEscapeHelper.java
@@ -0,0 +1,220 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.commons.compiler.source;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jetbrains.annotations.NotNull;
+
+public final class JavaEscapeHelper {
+
+    private static final Set<String> javaKeywords = new HashSet<>();
+    private static final Set<String> literals = new HashSet<>();
+    private static final Set<String> specialIdentifiers = new HashSet<>();
+    private static final Pattern ESCAPED_CHAR_PATTERN = Pattern.compile("(__[0-9a-f]{4}__)");
+
+    private JavaEscapeHelper() {
+    }
+
+    static {
+        javaKeywords.add("abstract");
+        javaKeywords.add("assert");
+        javaKeywords.add("boolean");
+        javaKeywords.add("break");
+        javaKeywords.add("byte");
+        javaKeywords.add("case");
+        javaKeywords.add("catch");
+        javaKeywords.add("char");
+        javaKeywords.add("class");
+        javaKeywords.add("const");
+        javaKeywords.add("continue");
+        javaKeywords.add("default");
+        javaKeywords.add("do");
+        javaKeywords.add("double");
+        javaKeywords.add("else");
+        javaKeywords.add("enum");
+        javaKeywords.add("extends");
+        javaKeywords.add("final");
+        javaKeywords.add("finally");
+        javaKeywords.add("float");
+        javaKeywords.add("for");
+        javaKeywords.add("goto");
+        javaKeywords.add("if");
+        javaKeywords.add("implements");
+        javaKeywords.add("import");
+        javaKeywords.add("instanceof");
+        javaKeywords.add("int");
+        javaKeywords.add("interface");
+        javaKeywords.add("long");
+        javaKeywords.add("native");
+        javaKeywords.add("new");
+        javaKeywords.add("package");
+        javaKeywords.add("private");
+        javaKeywords.add("protected");
+        javaKeywords.add("public");
+        javaKeywords.add("return");
+        javaKeywords.add("short");
+        javaKeywords.add("static");
+        javaKeywords.add("strictfp");
+        javaKeywords.add("super");
+        javaKeywords.add("switch");
+        javaKeywords.add("synchronized");
+        javaKeywords.add("this");
+        javaKeywords.add("throw");
+        javaKeywords.add("throws");
+        javaKeywords.add("transient");
+        javaKeywords.add("try");
+        javaKeywords.add("void");
+        javaKeywords.add("volatile");
+        javaKeywords.add("while");
+        javaKeywords.add("_");
+
+        literals.add("true");
+        literals.add("false");
+        literals.add("null");
+
+        specialIdentifiers.add("var");
+    }
+
+    /**
+     * Converts the given identifier to a legal Java identifier.
+     *
+     * @param identifier the identifier to convert
+     * @return legal Java identifier corresponding to the given identifier
+     */
+    public static @NotNull String getJavaIdentifier(@NotNull String identifier) {
+        StringBuilder modifiedIdentifier = new StringBuilder();
+        char[] identifierChars = new char[identifier.length()];
+        identifier.getChars(0, identifier.length(), identifierChars, 0);
+        for (int i = 0; i < identifierChars.length; i++) {
+            char ch = identifierChars[i];
+            if (i == 0 && !Character.isJavaIdentifierStart(ch)) {
+                modifiedIdentifier.append(escapeChar(ch));
+            } else {
+                if (!Character.isJavaIdentifierPart(ch)) {
+                    modifiedIdentifier.append(escapeChar(ch));
+                } else {
+                    modifiedIdentifier.append(ch);
+                }
+            }
+        }
+        String currentIdentifier = modifiedIdentifier.toString();
+        if (isJavaKeyword(currentIdentifier) || isJavaLiteral(currentIdentifier) || isSpecialIdentifier(currentIdentifier)) {
+            return escapeChar(currentIdentifier.charAt(0)) + currentIdentifier.substring(1);
+        }
+        return currentIdentifier;
+    }
+
+    /**
+     * Escapes the provided character so that it's a valid Java identifier character.
+     *
+     * @param ch the character to escape
+     * @return the escaped character representation
+     */
+    public static @NotNull String escapeChar(char ch) {
+        return String.format("__%04x__", (int) ch);
+    }
+
+    /**
+     * Provided an escaped string (obtained by calling {@link #escapeChar(char)}) it will will return the character that was
+     * escaped.
+     *
+     * @param escaped the escaped string
+     * @return the original character
+     */
+    public static char unescape(@NotNull String escaped) {
+        String toProcess = escaped.replace("__", "");
+        return (char) Integer.parseInt(toProcess, 16);
+    }
+
+    /**
+     * Provided a string which could contain characters escaped through {@link #escapeChar(char)}, this method will unescape all escaped
+     * characters.
+     *
+     * @param escaped a string containing escaped characters
+     * @return a string with all escaped sequences produced by {@link #escapeChar(char)} replaced by the original character
+     */
+    public static @NotNull String unescapeAll(@NotNull String escaped) {
+        String unescaped = escaped;
+        Matcher matcher = ESCAPED_CHAR_PATTERN.matcher(unescaped);
+        while (matcher.find()) {
+            String group = matcher.group();
+            char unescapedChar = unescape(group);
+            unescaped = unescaped.replace(group, Character.toString(unescapedChar));
+        }
+        return unescaped;
+    }
+
+    /**
+     * Converts the given {@code path} to a Java package or fully-qualified class name, depending on the {@code path}'s value.
+     *
+     * @param path the path to convert
+     * @return Java package corresponding to the given path
+     */
+    public static @NotNull String makeJavaPackage(@NotNull String path) {
+        String[] classNameComponents = path.split("/|\\\\");
+        StringBuilder legalClassNames = new StringBuilder();
+        for (int i = 0; i < classNameComponents.length; i++) {
+            String classNameComponent = classNameComponents[i];
+            if (classNameComponent.isEmpty()) {
+                continue;
+            }
+            legalClassNames.append(getJavaIdentifier(classNameComponent));
+            if (i < classNameComponents.length - 1) {
+                legalClassNames.append('.');
+            }
+        }
+        return legalClassNames.toString();
+    }
+
+    /**
+     * Test whether the argument is a Java keyword.
+     *
+     * @param key the String to test
+     * @return {@code true} if the String is a Java keyword, {@code false} otherwise
+     */
+    public static boolean isJavaKeyword(@NotNull String key) {
+        return javaKeywords.contains(key);
+    }
+
+    /**
+     * Test whether the argument is a Java literal (i.e. {@code true}, {@code false}, {@code null}).
+     *
+     * @param key the String to test
+     * @return {@code true} if the String is a Java literal, {@code false} otherwise
+     */
+    public static boolean isJavaLiteral(@NotNull String key) {
+        return literals.contains(key);
+    }
+
+    /**
+     * Test whether the argument is a special identifier (i.e. {@code var}).
+     *
+     * @param key the String to test
+     * @return {@code true} if the String is a Java special identifier, {@code false} otherwise
+     */
+    public static boolean isSpecialIdentifier(@NotNull String key) {
+        return specialIdentifiers.contains(key);
+    }
+
+
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/source/package-info.java b/src/main/java/org/apache/sling/commons/compiler/source/package-info.java
new file mode 100644
index 0000000..d58b643
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/source/package-info.java
@@ -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.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+@Version("1.0.0")
+package org.apache.sling.commons.compiler.source;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/commons/compiler/source/JavaEscapeHelperTest.java b/src/test/java/org/apache/sling/commons/compiler/source/JavaEscapeHelperTest.java
new file mode 100644
index 0000000..242a5d1
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/compiler/source/JavaEscapeHelperTest.java
@@ -0,0 +1,59 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.commons.compiler.source;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class JavaEscapeHelperTest {
+
+    @Test
+    public void testGetClassNameFromScriptPath() {
+        assertEquals("apps.projects.script__002e__html", JavaEscapeHelper.makeJavaPackage("/apps/projects/script.html"));
+        assertEquals("apps.projects.script__002e__html", JavaEscapeHelper.makeJavaPackage("\\apps\\projects\\script.html"));
+        assertEquals("apps.my__002d__project.script__002e__html", JavaEscapeHelper.makeJavaPackage("/apps/my-project/script.html"));
+        assertEquals("apps.my__002d__project.script__002e__html", JavaEscapeHelper.makeJavaPackage("\\apps\\my-project\\script.html"));
+        assertEquals("apps.my_project.script__002e__html", JavaEscapeHelper.makeJavaPackage("/apps/my_project/script.html"));
+        assertEquals("apps.my_project.script__002e__html", JavaEscapeHelper.makeJavaPackage("\\apps\\my_project\\script.html"));
+        assertEquals("apps.projects.__0031__.script__002e__html", JavaEscapeHelper.makeJavaPackage("/apps/projects/1/script.html"));
+        assertEquals("apps.projects.__0031__.script__002e__html", JavaEscapeHelper.makeJavaPackage("\\apps\\projects\\1\\script.html"));
+        assertEquals("apps.projects.__0073__witch.script__002e__html", JavaEscapeHelper.makeJavaPackage("/apps/projects/switch/script.html"));
+        assertEquals("apps.projects.__0073__witch.script__002e__html", JavaEscapeHelper.makeJavaPackage("\\apps\\projects\\switch\\script.html"));
+    }
+
+    @Test
+    public void testMakeJavaIdentifier() {
+        assertEquals("a__0020__b", JavaEscapeHelper.getJavaIdentifier("a b"));
+        assertEquals("__0074__rue", JavaEscapeHelper.getJavaIdentifier("true"));
+        assertEquals("__0076__ar", JavaEscapeHelper.getJavaIdentifier("var"));
+    }
+
+    @Test
+    public void testUnmangle() {
+        assertEquals('.', JavaEscapeHelper.unescape(JavaEscapeHelper.escapeChar('.')));
+        assertEquals('.', JavaEscapeHelper.unescape("__002e__"));
+    }
+
+    @Test
+    public void testUnescapeAll() {
+        assertEquals("apps.projects.switch.script.html", JavaEscapeHelper.unescapeAll("apps.projects.__0073__witch.script__002e__html"));
+        assertEquals("apps.projects.switch.script.html", JavaEscapeHelper.unescapeAll("apps.projects.switch.script.html"));
+    }
+}