/******************************************************************************* * Copyright © 2011, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.gen.javascript; import java.util.Properties; import org.eclipse.edt.mof.egl.Function; import org.eclipse.edt.mof.egl.Type; import org.eclipse.edt.mof.egl.utils.TypeUtils; /** * JavaScriptAliaser */ public class JavaScriptAliaser { /** Special characters we must escape to get valid JavaScript identifiers */ private static final String specialChars = "-@#"; /** A registry for the aliases that we have to use for JavaScript keywords. */ private static final Properties keywordCache = new Properties(); /** * A registry for the aliases that we have to use for part names, backed by * the keywordCache */ private static final Properties aliasCache = new Properties( keywordCache ); /** Maps class names that cannot be used in JavaScript to aliases. */ private static final Properties javascriptNames = new Properties(); private static final String EXTERNAL_TYPE_PREFIX = "eze$$"; //$NON-NLS-1$ static { // List is taken from http://javascript.about.com/library/blreserved.htm keywordCache.put( "abstract", "ezekw$$abstract" ); keywordCache.put( "as", "ezekw$$as" ); keywordCache.put( "boolean", "ezekw$$boolean" ); keywordCache.put( "break", "ezekw$$break" ); keywordCache.put( "byte", "ezekw$$byte" ); keywordCache.put( "case", "ezekw$$case" ); keywordCache.put( "catch", "ezekw$$catch" ); keywordCache.put( "char", "ezekw$$char" ); keywordCache.put( "class", "ezekw$$class" ); keywordCache.put( "continue", "ezekw$$continue" ); keywordCache.put( "const", "ezekw$$const" ); keywordCache.put( "debugger", "ezekw$$debugger" ); keywordCache.put( "default", "ezekw$$default" ); keywordCache.put( "delete", "ezekw$$delete" ); keywordCache.put( "do", "ezekw$$do" ); keywordCache.put( "dojo", "ezekw$$dojo" ); keywordCache.put( "dijit", "ezekw$$dijit" ); keywordCache.put( "dojox", "ezekw$$dojox" ); keywordCache.put( "double", "ezekw$$double" ); keywordCache.put( "else", "ezekw$$else" ); keywordCache.put( "enum", "ezekw$$enum" ); keywordCache.put( "export", "ezekw$$export" ); keywordCache.put( "extends", "ezekw$$extends" ); keywordCache.put( "false", "ezekw$$false" ); keywordCache.put( "final", "ezekw$$final" ); keywordCache.put( "finally", "ezekw$$finally" ); keywordCache.put( "float", "ezekw$$float" ); keywordCache.put( "for", "ezekw$$for" ); keywordCache.put( "function", "ezekw$$function" ); keywordCache.put( "goto", "ezekw$$goto" ); keywordCache.put( "if", "ezekw$$if" ); keywordCache.put( "implements", "ezekw$$implements" ); keywordCache.put( "import", "ezekw$$import" ); keywordCache.put( "in", "ezekw$$in" ); keywordCache.put( "instanceof", "ezekw$$instanceof" ); keywordCache.put( "int", "ezekw$$int" ); keywordCache.put( "interface", "ezekw$$interface" ); keywordCache.put( "is", "ezekw$$is" ); keywordCache.put( "long", "ezekw$$long" ); keywordCache.put( "namespace", "ezekw$$namespace" ); keywordCache.put( "native", "ezekw$$native" ); keywordCache.put( "new", "ezekw$$new" ); keywordCache.put( "null", "ezekw$$null" ); keywordCache.put( "package", "ezekw$$package" ); keywordCache.put( "private", "ezekw$$private" ); keywordCache.put( "protected", "ezekw$$protected" ); keywordCache.put( "public", "ezekw$$public" ); keywordCache.put( "return", "ezekw$$return" ); keywordCache.put( "short", "ezekw$$short" ); keywordCache.put( "static", "ezekw$$static" ); keywordCache.put( "super", "ezekw$$super" ); keywordCache.put( "switch", "ezekw$$switch" ); keywordCache.put( "synchronized", "ezekw$$synchronized" ); keywordCache.put( "this", "ezekw$$this" ); keywordCache.put( "throw", "ezekw$$throw" ); keywordCache.put( "throws", "ezekw$$throws" ); keywordCache.put( "transient", "ezekw$$transient" ); keywordCache.put( "true", "ezekw$$true" ); keywordCache.put( "try", "ezekw$$try" ); keywordCache.put( "typeof", "ezekw$$typeof" ); keywordCache.put( "use", "ezekw$$use" ); keywordCache.put( "var", "ezekw$$var" ); keywordCache.put( "void", "ezekw$$void" ); keywordCache.put( "volatile", "ezekw$$volatile" ); keywordCache.put( "while", "ezekw$$while" ); keywordCache.put( "with", "ezekw$$with" ); } static { // Initialize javascriptNames javascriptNames.put( "class", "className" ); } /** * Adds an alias for a special character to the buffer. The alias is the * escape char followed by the four-digit hex representation of the Unicode * character. * * @param buffer * the buffer to write the alias to. * @param c * the character to be aliased. * @param escapeChar * the escape character. */ private static void addCharacterAlias( StringBuffer buffer, char c, char escapeChar ) { buffer.append( escapeChar ); String hex = Integer.toHexString( c ); // For VG parts, uppercase it. if ( escapeChar == 'x' ) { hex = hex.toUpperCase(); } // Make sure we get four digits. if ( c < '\u0010' ) { buffer.append( "000" ); } else if ( c < '\u0100' ) { buffer.append( "00" ); } else if ( c < '\u1000' ) { buffer.append( '0' ); } buffer.append( hex ); } /** * Certain function names defined in EGL types (such as String.length) cannot be implemented as-is because they * conflict with the runtime language's existing (and un-overridable) definitions. This will check for these * special cases and return a Function object as appropriate. * * @param f * @return */ public static Function getAlias(Context ctx, Function f){ Function result = f; try { if ((f.getContainer() instanceof Type)) { Type type = ((Type)f.getContainer()).getClassifier(); if (TypeUtils.isTextType(type)) { if (f.getCaseSensitiveName().equals("length")) { result = (Function) f.clone(); result.setName("textLen"); } } } /* TODO sbg The logic below is part of https://bugs.eclipse.org/bugs/show_bug.cgi?id=358329, however, * it should eventually be removed when we implement * https://bugs.eclipse.org/bugs/show_bug.cgi?id=359315 */ // TODO sbg As written, this will cause overloads to overwrite method renames (above) { String overloaded = CommonUtilities.isOverloaded(ctx, f); if (overloaded != null){ result = (Function) f.clone(); result.setName(overloaded); } } } catch (Exception e) { result = f; } return result; } /** * Returns an alias for a part name, using '_' as the escape character. * * @param partName * @return either an alias for the part name, or the original part name if * it doesn't need an alias. */ public static String getAlias( String partName ) { return getAlias( partName, '_' ); } /** * Returns an alias for a part name. Aliases are only different from the * name when the name is a JavaScript keyword or it contains the characters * '-', '@', or '#'. JavaScript keywords are aliased with a "ezekw$$" prefix. * Invalid characters are aliased by replacing them with a string made from * the escape character plus the Unicode value of the aliased character in * hex. For example, if the escape character is '_', the alias for HELLO is * HELLO, the alias for throw is ezekw$$throw, and the alias for rice-a-roni * is rice_002da_002droni. * * @param partName * the name of the part. * @param escapeChar * the escape character to use. * @return either an alias for the part name, or the part name if it doesn't * need an alias. */ public static String getAlias( String partName, char escapeChar ) { // First check our cache of names so we don't have to examine each // character in the part name every time. String alias = JavaScriptAliaser.aliasCache.getProperty( partName ); if ( alias != null ) { return alias; } // We have to examine the part name to make an alias for it. char[] chars = partName.toCharArray(); int start = 0; StringBuffer buffer = null; for ( int i = 0; i < chars.length; i++ ) { if ( chars[ i ] != escapeChar && specialChars.indexOf( chars[ i ] ) >= 0 ) { if ( buffer == null ) { // Make a buffer to hold the alias in. buffer = new StringBuffer( chars.length + 16 ); } // Put the characters that don't need to be aliased into the // buffer. buffer.append( chars, start, i - start ); // Put the alias for this character into the buffer. JavaScriptAliaser.addCharacterAlias( buffer, chars[ i ], escapeChar ); start = i + 1; } } // Save the value in the cache and return it. if ( buffer == null ) { // No alias was needed. alias = partName; } else { // An alias was needed. buffer.append( chars, start, chars.length - start ); alias = buffer.toString(); } JavaScriptAliaser.aliasCache.put( partName, alias ); return alias; } public static String getAliasForExternalType( String partName ){ return EXTERNAL_TYPE_PREFIX + partName; } /** * This is the same as getAlias, with one more step. The value it returns will * not be one of the special names we don't want to reuse. * * @param partName * the name of the part. * @return either an alias for the part name, or the part name if it doesn't * need an alias. */ public static String getJavascriptSafeAlias( String partName ) { String alias = JavaScriptAliaser.getAlias( partName ); return JavaScriptAliaser.javascriptNames.getProperty( alias, alias ); } /** * Returns whether name is a JavaScript keyword that we should use an alias * for. * * @param name * the name to check. * @return true if so */ public static boolean isJavaScriptKeyword( String name ) { return JavaScriptAliaser.keywordCache.containsKey( name ); } /** * Construct a String made from packageName, lowercased, with each part * aliased. * * @param packageName * @return The massaged package name. */ public static String packageNameAlias( String packageName ) { String unAliased = packageName.toLowerCase(); String aliased = ""; int dotIndex = unAliased.indexOf( '.' ); if ( dotIndex == -1 ) { aliased = getJavascriptSafeAlias( unAliased ); } else { while ( dotIndex != -1 ) { String piece = unAliased.substring( 0, dotIndex ); aliased += getJavascriptSafeAlias( piece ) + "."; unAliased = unAliased.substring( dotIndex + 1 ); dotIndex = unAliased.indexOf( '.' ); } aliased = aliased + getJavascriptSafeAlias( unAliased ); } return aliased; } /** * Return the package name for a directory path, names lowercased with each * folder separated by the specified separator character. * * @param pkg * array of folder names * @param separator * Character separator * @return Package name */ public static String packageNameAlias( String[] pkg, char separator) { return packageNameAlias( pkg, separator, true ); } public static String packageNameAlias( String[] pkg, char separator , boolean toLowerCase) { StringBuffer buff = new StringBuffer(); for ( int i = 0; i < pkg.length; i++ ) { if ( i > 0 ) { buff.append( separator ); } String alias = getJavascriptSafeAlias( pkg[ i ] ); if (toLowerCase) alias = alias.toLowerCase(); buff.append( alias ); } return buff.toString(); } }