/** * 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.drools.semantics.utils; import org.drools.semantics.Literal; import javax.xml.ws.Holder; import java.net.URI; import java.net.URISyntaxException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; public final class NameUtils { public enum IdentifierType { CLASS, INTERFACE, GETTER, SETTER, VARIABLE, CONSTANT }; public static final String JAXB_URI = "http://java.sun.com/xml/ns/jaxb"; private static final char[] XML_NAME_PUNCTUATION_CHARS = new char[] { /* hyphen */ '\u002D', /* period */ '\u002E', /* colon */'\u003A', /* dot */ '\u00B7', /* greek ano teleia */ '\u0387', /* arabic end of ayah */ '\u06DD', /* arabic start of rub el hizb */ '\u06DE', /* underscore */ '\u005F', }; private static final String XML_NAME_PUNCTUATION_STRING = new String(XML_NAME_PUNCTUATION_CHARS); private static final Map<String, String> BUILTIN_DATATYPES_MAP; private static final Map<String, Class<?>> HOLDER_TYPES_MAP; private static final String NEG = "ObjectComplementOf"; static { BUILTIN_DATATYPES_MAP = new HashMap<String, String>(); BUILTIN_DATATYPES_MAP.put( "string", "java.lang.String" ); BUILTIN_DATATYPES_MAP.put( "integer", "java.math.BigInteger" ); BUILTIN_DATATYPES_MAP.put( "int", "int" ); BUILTIN_DATATYPES_MAP.put( "long", "long" ); BUILTIN_DATATYPES_MAP.put( "short", "short" ); BUILTIN_DATATYPES_MAP.put( "decimal", "java.math.BigDecimal" ); BUILTIN_DATATYPES_MAP.put( "float", "float" ); BUILTIN_DATATYPES_MAP.put( "double", "double" ); BUILTIN_DATATYPES_MAP.put( "boolean", "boolean" ); BUILTIN_DATATYPES_MAP.put( "byte", "byte" ); BUILTIN_DATATYPES_MAP.put( "QName", "javax.xml.namespace.QName" ); BUILTIN_DATATYPES_MAP.put( "dateTime", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "base64Binary", "byte[]" ); BUILTIN_DATATYPES_MAP.put( "hexBinary", "byte[]" ); BUILTIN_DATATYPES_MAP.put( "unsignedInt", "long" ); BUILTIN_DATATYPES_MAP.put( "unsignedShort", "short" ); BUILTIN_DATATYPES_MAP.put( "unsignedByte", "byte" ); BUILTIN_DATATYPES_MAP.put( "time", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "date", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "gYear", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "gYearMonth", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "gMonth", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "gMonthDay", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "gDay", "javax.xml.datatype.XMLGregorianCalendar" ); BUILTIN_DATATYPES_MAP.put( "duration", "javax.xml.datatype.Duration" ); BUILTIN_DATATYPES_MAP.put( "NOTATION", "javax.xml.namespace.QName" ); HOLDER_TYPES_MAP = new HashMap<String, Class<?>>(); HOLDER_TYPES_MAP.put( "int", java.lang.Integer.class ); HOLDER_TYPES_MAP.put( "long", java.lang.Long.class ); HOLDER_TYPES_MAP.put( "short", java.lang.Short.class ); HOLDER_TYPES_MAP.put( "float", java.lang.Float.class ); HOLDER_TYPES_MAP.put( "double", java.lang.Double.class ); HOLDER_TYPES_MAP.put( "boolean", java.lang.Boolean.class ); HOLDER_TYPES_MAP.put( "byte", java.lang.Byte.class ); } /** Use this character as suffix */ static final char KEYWORD_PREFIX = '_'; /** * These are java keywords as specified at the following URL. * http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#229308 * Note that false, true, and null are not strictly keywords; they are * literal values, but for the purposes of this array, they can be treated * as literals. */ private static final Set<String> KEYWORDS = new HashSet<String>(Arrays.asList( "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "void", "volatile", "while" )); private static NameUtils instance; public static NameUtils getInstance() { if ( instance == null ) { instance = new NameUtils(); } return instance; } private NameUtils() { } /** * checks if the input string is a valid java keyword. * * @return boolean true/false */ public static boolean isJavaKeyword(String keyword) { return KEYWORDS.contains(keyword); } /** * Turn a java keyword string into a non-Java keyword string. (Right now * this simply means appending an underscore.) */ public static String makeNonJavaKeyword(String keyword) { return KEYWORD_PREFIX + keyword; } public static String builtInTypeToJavaType( String type ) { return BUILTIN_DATATYPES_MAP.get( type ); } public static String builtInTypeToWrappingJavaType( String type ) { type = type.replaceAll( "xsd:", "" ); String klass = BUILTIN_DATATYPES_MAP.get( type ); if ( HOLDER_TYPES_MAP.containsKey( klass ) ) { return HOLDER_TYPES_MAP.get( klass ).getName(); } else { return klass; } } public static Class<?> holderClass( String type ) { return HOLDER_TYPES_MAP.get( type ); } public static String buildFQNameFromIri( String iri ) { return nameToIdentifier( iri, IdentifierType.VARIABLE ); } public static String buildLowCaseNameFromIri( String iri ) { return nameToIdentifier( iri, IdentifierType.VARIABLE ); } public static String buildNameFromIri( String iriStart, String iriFragment ) { String iri = iriFragment != null ? iriFragment : iriStart; return nameToIdentifier( iri, IdentifierType.CLASS ); } public static String separatingName( String name ) { return name.endsWith( "/" ) || name.endsWith( "#" ) ? name : ( name + "#" ); } public static URI packageToNamespaceURI( String pack ) { return URI.create( "http://" + pack.replace( ".", "/" ) ); } /** * Generates a Java package name from a URI according to the * algorithm outlined in JAXB 2.0. * * @param namespaceURI the namespace URI. * @return the package name. */ public static String namespaceURIToPackage( String namespaceURI ) { try { return nameSpaceURIToPackage( new URI( namespaceURI ) ); } catch ( URISyntaxException ex ) { return null; } } /** * Generates a Java package name from a URI according to the * algorithm outlined in JAXB 2.0. * * @param uri the namespace URI. * @return the package name. */ public static String nameSpaceURIToPackage( URI uri ) { StringBuffer packageName = new StringBuffer(); String authority = uri.getAuthority(); if ( authority == null && "urn".equals( uri.getScheme() ) ) { authority = uri.getSchemeSpecificPart(); } if ( null != authority && ! "".equals( authority ) ) { if ( "urn".equals( uri.getScheme() ) ) { packageName.append( authority ); for ( int i = 0; i < packageName.length(); i++ ) { if ( packageName.charAt( i ) == '-' ) { packageName.setCharAt( i, '.' ); } } authority = packageName.toString(); packageName.setLength( 0 ); StringTokenizer st = new StringTokenizer( authority, ":" ); while ( st.hasMoreTokens() ) { String token = st.nextToken(); if ( packageName.length() > 0 ) { packageName.insert( 0, "." ); packageName.insert( 0, normalizePackageNamePart( token ) ); } else { packageName.insert( 0, token ); } } authority = packageName.toString(); packageName.setLength( 0 ); } StringTokenizer st = new StringTokenizer( authority, "." ); if ( st.hasMoreTokens() ) { String token = null; while ( st.hasMoreTokens() ) { token = st.nextToken(); if ( packageName.length() == 0 ) { if ( "www".equals( token ) ) { continue; } } else { packageName.insert( 0, "." ); } packageName.insert( 0, normalizePackageNamePart( token ) ); } } } String path = uri.getPath(); if ( path == null ) { path = ""; } int index = path.lastIndexOf( '.' ); if ( index < 0 ) { index = path.length(); } StringTokenizer st = new StringTokenizer(path.substring( 0, index ), "/" ); while ( st.hasMoreTokens() ) { String token = st.nextToken(); if ( packageName.length() > 0 ) { packageName.append( '.' ); } packageName.append( normalizePackageNamePart( token ) ); } return packageName.toString(); } private static String normalizePackageNamePart( String name ) { StringBuffer sname = new StringBuffer( name.toLowerCase() ); for ( int i = 0; i < sname.length(); i++ ) { sname.setCharAt( i, Character.toLowerCase( sname.charAt( i ) ) ); } for ( int i = 0; i < sname.length(); i++ ) { if ( ! Character.isJavaIdentifierPart( sname.charAt( i ) ) ) { sname.setCharAt( i, '_' ); } } if ( isJavaKeyword( sname.toString() ) ) { sname.insert( 0, '_' ); } if ( ! Character.isJavaIdentifierStart( sname.charAt( 0 ) ) ) { sname.insert( 0, '_' ); } return sname.toString(); } /** * Converts an XML name to a Java identifier according to the mapping * algorithm outlines in the JAXB specification * * @param name the XML name * @return the Java identifier */ public static String nameToIdentifier( String name, IdentifierType type ) { if ( null == name || name.length() == 0 ) { return name; } // algorithm will not change an XML name that is already a legal and // conventional (!) Java class, method, or constant identifier boolean legalIdentifier = false; StringBuffer buf = new StringBuffer( name ); legalIdentifier = Character.isJavaIdentifierStart( buf.charAt( 0 ) ); for ( int i = 1; i < name.length() && legalIdentifier; i++ ) { legalIdentifier = legalIdentifier && Character.isJavaIdentifierPart( buf.charAt( i ) ); } boolean conventionalIdentifier = isConventionalIdentifier( buf, type ); if ( legalIdentifier && conventionalIdentifier ) { if ( NameUtils.isJavaKeyword( name ) && type == IdentifierType.VARIABLE ) { name = normalizePackageNamePart( name.toString() ); } return name; } // split into words List<String> words = new ArrayList<String>(); StringTokenizer st = new StringTokenizer( name, XML_NAME_PUNCTUATION_STRING ); while ( st.hasMoreTokens() ) { words.add( st.nextToken() ); } for ( int i = 0; i < words.size(); i++ ) { splitWord( words, i ); } return makeConventionalIdentifier( words, type ); } private static void splitWord( List<String> words, int listIndex ) { String word = words.get( listIndex ); if ( word.length() <= 1 ) { return; } int index = listIndex + 1; StringBuffer sword = new StringBuffer( word ); int first = 0; char firstChar = sword.charAt( first ); if ( Character.isLowerCase( firstChar ) ) { sword.setCharAt( first, Character.toUpperCase( firstChar ) ); } int i = 1; while ( i < sword.length() ) { if ( Character.isDigit( firstChar ) ) { while ( i < sword.length() && Character.isDigit( sword.charAt( i ) ) ) { i++; } } else if ( isCasedLetter( firstChar ) ) { boolean previousIsLower = Character.isLowerCase( firstChar ); while ( i < sword.length() && isCasedLetter( sword.charAt( i ) ) ) { if ( Character.isUpperCase( sword.charAt( i ) ) && previousIsLower ) { break; } previousIsLower = Character.isLowerCase( sword.charAt( i ) ); i++; } } else { // first must be a mark or an uncased letter while ( i < sword.length() && ( isMark( sword.charAt( i ) ) || ! isCasedLetter( sword.charAt( i ) ) ) ) { i++; } } // characters from first to i are all either // * digits // * upper or lower case letters, with only the first one an upper // * uncased letters or marks String newWord = sword.substring( first, i ); words.add( index, newWord ); index++; if ( i >= sword.length() ) { break; } else { first = i; firstChar = sword.charAt( first ); } } if ( index > ( listIndex + 1 ) ) { words.remove( listIndex ); } } private static boolean isMark( char c ) { return Character.isJavaIdentifierPart( c ) && ! Character.isLetter( c ) && ! Character.isDigit( c ); } private static boolean isCasedLetter( char c ) { return Character.isUpperCase( c ) || Character.isLowerCase( c ); } private static boolean isConventionalIdentifier( StringBuffer buf, IdentifierType type ) { if ( null == buf || buf.length() == 0 ) { return false; } boolean result = false; if (IdentifierType.CONSTANT == type) { for (int i = 0; i < buf.length(); i++) { if (Character.isLowerCase(buf.charAt(i))) { return false; } } result = true; } else if (IdentifierType.VARIABLE == type) { result = Character.isLowerCase(buf.charAt(0)); } else { int pos = 3; if (IdentifierType.GETTER == type && !(buf.length() >= pos && "get".equals(buf.subSequence(0, 3)))) { return false; } else if (IdentifierType.SETTER == type && !(buf.length() >= pos && "set".equals(buf.subSequence(0, 3)))) { return false; } else { pos = 0; } result = Character.isUpperCase(buf.charAt(pos)); } return result; } private static String makeConventionalIdentifier( List<String> words, IdentifierType type ) { StringBuffer buf = new StringBuffer(); boolean firstWord = true; if ( IdentifierType.GETTER == type ) { buf.append( "get" ); } else if ( IdentifierType.SETTER == type ) { buf.append( "set" ); } for ( String w : words ) { int l = buf.length(); if ( l > 0 && IdentifierType.CONSTANT == type ) { buf.append( '_' ); l++; } buf.append( w ); if ( IdentifierType.CONSTANT == type ) { for ( int i = l; i < buf.length(); i++ ) { if ( Character.isLowerCase( buf.charAt( i ) ) ) { buf.setCharAt( i, Character.toUpperCase( buf.charAt( i ) ) ); } } } else if ( IdentifierType.VARIABLE == type ) { if ( firstWord && Character.isUpperCase( buf.charAt( l ) ) ) { buf.setCharAt( l, Character.toLowerCase( buf.charAt( l ) ) ); } } else { if ( firstWord && Character.isLowerCase( buf.charAt( l ) ) ) { buf.setCharAt( l, Character.toUpperCase( buf.charAt( l ) ) ); } } firstWord = false; } return buf.toString(); } public static Class<?> getValidClass( Class<?> cls ) { if ( cls.isEnum() ) { return cls; } if ( cls.isArray() ) { return cls; } if ( cls == Object.class || cls == String.class || cls == Holder.class ) { cls = null; } else if ( cls.isPrimitive() || cls.isAnnotation() ) { cls = null; } else if ( cls.isInterface() ) { return cls; } if ( cls != null ) { if ( cls.getName().equals("javax.xml.ws.wsaddressing.W3CEndpointReference") ) { return cls; } try { if ( cls.getConstructor( new Class[0] ) == null ) { cls = null; } } catch ( NoSuchMethodException ex ) { cls = null; } } return cls; } public static String capitalize( String s ) { StringTokenizer tok = new StringTokenizer( s, "_" ); String upName = tok.nextToken(); upName = upName.substring( 0, 1 ).toUpperCase() + upName.substring( 1 ); while ( tok.hasMoreTokens() ) { String word = tok.nextToken(); upName += "_" + word.substring( 0, 1 ).toUpperCase() + word.substring( 1 ); } return s.startsWith( "_" ) ? "_" + upName : upName; } public static String reverse(String aPackage) { StringTokenizer tok = new StringTokenizer( aPackage, "." ); String ans = tok.nextToken(); while ( tok.hasMoreTokens() ) { ans = tok.nextToken() + "." + ans; } return ans; } public static String compactUpperCase( String name ) { String idName = nameToIdentifier( name, IdentifierType.CLASS ); StringTokenizer tok = new StringTokenizer( idName, "_" ); String upName = tok.nextToken(); while ( tok.hasMoreTokens() ) { String word = tok.nextToken(); upName += "_" + word.substring( 0, 1 ).toUpperCase() + word.substring( 1 ); } return upName; } public static String compactVariable( String name ) { String idName = nameToIdentifier( name, IdentifierType.VARIABLE ); StringTokenizer tok = new StringTokenizer( idName, "_" ); String upName = tok.nextToken(); while ( tok.hasMoreTokens() ) { String word = tok.nextToken(); upName += word.substring( 0, 1 ).toUpperCase() + word.substring( 1 ); } return upName; } public static String getter( String name, String type ) { return getter( name, type, null ); } public static String setter( String name ) { return "set" + capitalize( name ); } public static String getter( String name, String type, Integer max ) { String prefix = ( ( max != null && max == 1 ) && ( type.equals("xsd:boolean") || type.equals( boolean.class.getName() ) || type.equals( Boolean.class.getName() ) ) ) ? "is" : "get"; return prefix + capitalize( name ); } public static String negate( String expr ) { if ( expr.startsWith(NEG) ) { return expr.substring(NEG.length()+1,expr.length()-1); } else { return NEG + "(" + expr + ")"; } } public static String map( String dataType, boolean box ) { if ( "xsd:integer".equals( dataType ) ) { return "java.math.BigInteger"; } else if ( "xsd:int".equals( dataType ) ) { return box ? "java.lang.Integer" : "int"; } else if ( "xsd:string".equals( dataType ) ) { return "java.lang.String"; } else if ( "xsd:anyURI".equals( dataType ) ) { return "java.lang.String"; } else if ( "xsd:dateTime".equals( dataType ) ) { return "java.util.Date"; } else if ( "xsd:date".equals( dataType ) ) { return "java.util.Date"; } else if ( "xsd:time".equals( dataType ) ) { return "java.util.Date"; } else if ( "xsd:long".equals( dataType ) ) { return box ? "java.lang.Long" : "long"; } else if ( "xsd:float".equals( dataType ) ) { return box ? "java.lang.Float" : "float"; } else if ( "xsd:double".equals( dataType ) ) { return box ? "java.lang.Double" : "double"; } else if ( "xsd:short".equals( dataType ) ) { return box ? "java.lang.Short" : "short"; } else if ( "xsd:anySimpleType".equals( dataType ) ) { return Literal.class.getName(); } else if ( "xsd:boolean".equals( dataType ) ) { return box ? "java.lang.Boolean" : "boolean" ; } else if ( "xsd:byte".equals( dataType ) ) { return box ? "java.lang.Byte" : "byte"; } else if ( "xsd:decimal".equals( dataType ) ) { return "java.math.BigDecimal"; } else if ( "xsd:unsignedByte".equals( dataType ) ) { return box ? "java.lang.Short" : "short"; } else if ( "xsd:unsignedShort".equals( dataType ) ) { return box ? "java.lang.Integer" : "int"; } else if ( "xsd:unsignedInt".equals( dataType ) ) { return box ? "java.lang.Long" : "long"; } else { return dataType; } } public static String skolem( String dataType ) { if ( "xsd:integer".equals( dataType ) ) { return "null"; } else if ( "xsd:int".equals( dataType ) ) { return "null"; } else if ( "xsd:string".equals( dataType ) ) { return "null"; } else if ( "xsd:anyURI".equals( dataType ) ) { return "null"; } else if ( "xsd:dateTime".equals( dataType ) ) { return "null"; } else if ( "xsd:date".equals( dataType ) ) { return "null"; } else if ( "xsd:time".equals( dataType ) ) { return "null"; } else if ( "xsd:long".equals( dataType ) ) { return "null"; } else if ( "xsd:float".equals( dataType ) ) { return "null"; } else if ( "xsd:double".equals( dataType ) ) { return "null"; } else if ( "xsd:short".equals( dataType ) ) { return "null"; } else if ( "xsd:anySimpleType".equals( dataType ) ) { return "null"; } else if ( "xsd:boolean".equals( dataType ) ) { return "null"; } else if ( "xsd:byte".equals( dataType ) ) { return "null"; } else if ( "xsd:decimal".equals( dataType ) ) { return "null"; } else if ( "xsd:unsignedByte".equals( dataType ) ) { return "null"; } else if ( "xsd:unsignedShort".equals( dataType ) ) { return "null"; } else if ( "xsd:unsignedInt".equals( dataType ) ) { return "null"; } else { return "new " + dataType + "Impl()"; } } public static String nullify( String dataType, boolean box) { if ( box ) { return "null"; } if ( "xsd:integer".equals( dataType ) ) { return "java.math.BigInteger.ZERO"; } else if ( "xsd:int".equals( dataType ) ) { return "0"; } else if ( "xsd:string".equals( dataType ) ) { return "new String()"; } else if ( "xsd:dateTime".equals( dataType ) ) { return "null"; } else if ( "xsd:anyURI".equals( dataType ) ) { return "null"; } else if ( "xsd:date".equals( dataType ) ) { return "null"; } else if ( "xsd:time".equals( dataType ) ) { return "0"; } else if ( "xsd:long".equals( dataType ) ) { return "0"; } else if ( "xsd:float".equals( dataType ) ) { return "0.0f"; } else if ( "xsd:double".equals( dataType ) ) { return "0.0"; } else if ( "xsd:short".equals( dataType ) ) { return "0"; } else if ( "xsd:anySimpleType".equals( dataType ) ) { return "null"; } else if ( "xsd:boolean".equals( dataType ) ) { return "false"; } else if ( "xsd:byte".equals( dataType ) ) { return "0"; } else if ( "xsd:decimal".equals( dataType ) ) { return "null"; } else if ( "xsd:unsignedByte".equals( dataType ) ) { return "0"; } else if ( "xsd:unsignedShort".equals( dataType ) ) { return "0"; } else if ( "xsd:unsignedInt".equals( dataType ) ) { return "0"; } else { return "null"; } } private static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); public static void setDateFormatter( DateFormat format ) { formatter = format; } public static DateFormat getFormatter() { return formatter; } public static String marshalDate( Date d ) { return formatter.format( d ); } public static Date unmarshalDate( String d ) { try { return formatter.parse( d ); } catch (ParseException e) { e.printStackTrace(); } return new Date( 0 ); } }