diff --git a/java.source/apichanges.xml b/java.source/apichanges.xml --- a/java.source/apichanges.xml +++ b/java.source/apichanges.xml @@ -3,7 +3,7 @@ + + + Added support for encoding/decoding exotic identifiers to TreeUtilities. + + + + + + Added support for encoding/decoding exotic identifiers to TreeUtilities. + + + + Added GeneratorUtilities.copyComments method. diff --git a/java.source/nbproject/project.properties b/java.source/nbproject/project.properties --- a/java.source/nbproject/project.properties +++ b/java.source/nbproject/project.properties @@ -1,6 +1,6 @@ # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. # -# Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 1997-2010 Sun Microsystems, Inc. All rights reserved. # # The contents of this file are subject to the terms of either the GNU # General Public License Version 2 only ("GPL") or the Common @@ -23,7 +23,7 @@ # Contributor(s): # # The Original Software is NetBeans. The Initial Developer of the Original -# Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2010 Sun # Microsystems, Inc. All Rights Reserved. # # If you wish your version of this file to be governed by only the CDDL @@ -43,7 +43,7 @@ javadoc.title=Java Source javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=0.55.0 +spec.version.base=0.56.0 test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/javac-api-nb-7.0-b07.jar test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\ ${o.n.core.dir}/lib/boot.jar:\ diff --git a/java.source/src/org/netbeans/api/java/source/TreeUtilities.java b/java.source/src/org/netbeans/api/java/source/TreeUtilities.java --- a/java.source/src/org/netbeans/api/java/source/TreeUtilities.java +++ b/java.source/src/org/netbeans/api/java/source/TreeUtilities.java @@ -66,13 +66,14 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; +import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.api.lexer.Token; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.modules.java.source.builder.CommentHandlerService; import org.netbeans.modules.java.source.builder.CommentSetImpl; import org.netbeans.modules.java.source.parsing.SourceFileObject; -import org.netbeans.modules.java.source.query.CommentSet.RelativePosition; import org.openide.util.Exceptions; /** @@ -715,6 +716,129 @@ } } + /**Decode escapes defined in: http://wikis.sun.com/display/mlvm/ProjectCoinProposal, 3.1-3.9. + * Must be a full token text, including possible #". + * + * @param text to decode + * @return decoded escapes from the identifier + * @see http://wikis.sun.com/display/mlvm/ProjectCoinProposal + * @since 0.56 + */ + public @NonNull CharSequence decodeIdentifier(@NonNull CharSequence text) { + return decodeIdentifierInternal(text); + } + + /**Encode identifier using escapes defined in: http://wikis.sun.com/display/mlvm/ProjectCoinProposal, 3.1-3.9. + * + * @param text to encode + * @return encoded identifier, including #" if necessary + * @see http://wikis.sun.com/display/mlvm/ProjectCoinProposal + * @since 0.56 + */ + public @NonNull CharSequence encodeIdentifier(@NonNull CharSequence ident) { + return encodeIdentifierInternal(ident); + } + + static @NonNull CharSequence decodeIdentifierInternal(@NonNull CharSequence text) { + if (text.charAt(0) != '#') { + return text; + } + + int count = text.charAt(text.length() - 1) == '"' ? text.length() - 1 : text.length(); + StringBuilder sb = new StringBuilder(text.length()); + + for (int c = 2; c < count; c++) { + if (text.charAt(c) == '\\' && ++c < count) { + if (EXOTIC_ESCAPE.contains(text.charAt(c))) { + sb.append('\\'); + sb.append(text.charAt(c)); + } else { + //XXX: handle \012 + Character remaped = ESCAPE_UNENCODE.get(text.charAt(c)); + + if (remaped != null) { + sb.append(remaped); + } else { + //TODO: illegal? + sb.append(text.charAt(c)); + } + } + } else { + sb.append(text.charAt(c)); + } + } + + return sb.toString(); + } + + static @NonNull CharSequence encodeIdentifierInternal(@NonNull CharSequence ident) { + if (ident.length() == 0) { + //??? + return ident; + } + + StringBuilder sb = new StringBuilder(ident.length()); + boolean needsExotic = Character.isJavaIdentifierStart(ident.charAt(0)); + + //XXX: code points? + for (int i = 0; i < ident.length(); i++) { + char c = ident.charAt(i); + + if (Character.isJavaIdentifierPart(c)) { + sb.append(c); + continue; + } + + needsExotic = true; + + Character target = ESCAPE_ENCODE.get(c); + + if (target != null) { + sb.append('\\'); + sb.append(target); + } else { + sb.append(c); + } + } + + if (needsExotic) { + sb.append("\""); + sb.insert(0, "#\""); + + return sb.toString(); + } else { + return ident; + } + } + + static Set EXOTIC_ESCAPE = new HashSet( + Arrays.asList('!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '-', + ':', '=', '?', '@', '^', '_', '`', '{', '|', '}') + ); + + private static final Map ESCAPE_UNENCODE; + private static final Map ESCAPE_ENCODE; + + static { + Map unencode = new HashMap(); + + unencode.put('n', '\n'); + unencode.put('t', '\t'); + unencode.put('b', '\b'); + unencode.put('r', '\r'); + + ESCAPE_UNENCODE = Collections.unmodifiableMap(unencode); + + Map encode = new HashMap(); + + encode.put('\n', 'n'); + encode.put('\t', 't'); + encode.put('\b', 'b'); + encode.put('\r', 'r'); + + ESCAPE_ENCODE = Collections.unmodifiableMap(encode); + } + private void copyInnerClassIndexes(Tree from, Tree to) { final int[] fromIdx = {-2}; TreeScanner scanner = new TreeScanner() { diff --git a/java.source/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java b/java.source/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java --- a/java.source/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java +++ b/java.source/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java @@ -430,4 +430,23 @@ assertEquals(golden, uncaughtExceptionStrings); } + + public void testExoticIdentifiers() throws Exception { + performExoticIdentiferDecodeTest("#\"a\"", "a"); + for (char c : TreeUtilities.EXOTIC_ESCAPE) { + performExoticIdentiferDecodeTest("#\"\\" + c + "\"", "\\" + c); + } + performExoticIdentiferDecodeTest("#\"\\n\"", "\n"); + + performExoticIdentiferDecodeTest("a", "a"); + performExoticIdentiferEncodeTest("\n", "#\"\\n\""); + } + + private void performExoticIdentiferDecodeTest(String exotic, String golden) throws Exception { + assertEquals(golden, TreeUtilities.decodeIdentifierInternal(exotic).toString()); + } + + private void performExoticIdentiferEncodeTest(String exotic, String golden) throws Exception { + assertEquals(golden, TreeUtilities.encodeIdentifierInternal(exotic).toString()); + } }