Java tutorial
package org.op4j.devutils.selected; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.op4j.functions.FnArray; import org.op4j.functions.FnList; import org.op4j.functions.FnMap; import org.op4j.functions.FnSet; import org.op4j.functions.Function; import org.op4j.operators.impl.AbstractOperator; import org.op4j.operators.qualities.UniqFnOperator; import org.op4j.operators.qualities.UniqOpOperator; import org.op4j.operators.qualities.UniqOperator; import org.op4j.target.Target; import org.op4j.target.Target.Normalisation; import org.op4j.target.Target.Structure; import org.op4j.util.NormalisationUtils; public class ImplFile { public enum LevelStructure { ARRAY, LIST, SET, MAP, MAP_ENTRY, ELEMENTS, LEVEL_DOES_NOT_EXIST } private static final String LICENSE_HEADER = "/* \n" + " * ============================================================================= \n" + " * \n" + " * Copyright (c) 2010, The OP4J team (http://www.op4j.org) \n" + " * \n" + " * Licensed under the Apache License, Version 2.0 (the \"License\"); \n" + " * you may not use this file except in compliance with the License. \n" + " * You may obtain a copy of the License at \n" + " * \n" + " * http://www.apache.org/licenses/LICENSE-2.0 \n" + " * \n" + " * Unless required by applicable law or agreed to in writing, software \n" + " * distributed under the License is distributed on an \"AS IS\" BASIS, \n" + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n" + " * See the License for the specific language governing permissions and \n" + " * limitations under the License. \n" + " * \n" + " * ============================================================================= \n" + " */ \n"; private final ImplType implType; private final String packageName; private final Set<String> imports; private final Set<String> methodNames; private final List<String> methodImplementations; private final String className; private final TypeRep interfaceTypeRep; private String element; private String currentLevelType; private String currentLevelElement; private static Map<String, String[]> paramNames; private static Set<String> varargsPositions; private static Set<String> arrayTypeRequired; private static Map<String, LevelStructure> currentLevelsByPrefix; private static Map<String, LevelStructure> previousLevelsByPrefix; static { paramNames = new HashMap<String, String[]>(); paramNames.put("ifIndex", new String[] { "indexes" }); paramNames.put("ifTrue", new String[] { "eval" }); paramNames.put("ifFalse", new String[] { "eval" }); paramNames.put("ifNullOrFalse", new String[] { "eval" }); paramNames.put("ifNotNullAndFalse", new String[] { "eval" }); paramNames.put("ifNullOrTrue", new String[] { "eval" }); paramNames.put("ifIndexNot", new String[] { "indexes" }); paramNames.put("ifNotNullAndTrue", new String[] { "eval" }); paramNames.put("ifKeyEquals", new String[] { "keys" }); paramNames.put("ifKeyNotEquals", new String[] { "keys" }); paramNames.put("add", new String[] { "newElement" }); paramNames.put("addAll", new String[] { "newElements" }); paramNames.put("insert", new String[] { "position", "newElement" }); paramNames.put("insertAll", new String[] { "position", "newElements" }); paramNames.put("insert%3", new String[] { "position", "newKey", "newValue" }); paramNames.put("insertAll%int,Map", new String[] { "position", "map" }); paramNames.put("addAll%Collection", new String[] { "collection" }); paramNames.put("removeAllIndexes", new String[] { "indexes" }); paramNames.put("removeAllEqual", new String[] { "values" }); paramNames.put("removeAllTrue", new String[] { "eval" }); paramNames.put("removeAllFalse", new String[] { "eval" }); paramNames.put("removeAllNullOrFalse", new String[] { "eval" }); paramNames.put("removeAllNotNullAndFalse", new String[] { "eval" }); paramNames.put("removeAllNotNullAndTrue", new String[] { "eval" }); paramNames.put("removeAllNullOrTrue", new String[] { "eval" }); paramNames.put("removeAllIndexesNot", new String[] { "indexes" }); paramNames.put("removeAllKeys", new String[] { "keys" }); paramNames.put("removeAllKeysNot", new String[] { "keys" }); paramNames.put("execIfNotNull", new String[] { "function" }); paramNames.put("exec", new String[] { "function" }); paramNames.put("execIfNotNullAsArray", new String[] { "function" }); paramNames.put("execAsArray", new String[] { "function" }); paramNames.put("execIfNotNullAsList", new String[] { "function" }); paramNames.put("execAsList", new String[] { "function" }); paramNames.put("execIfNotNullAsMap", new String[] { "function" }); paramNames.put("execAsMap", new String[] { "function" }); paramNames.put("execIfNotNullAsMapEntry", new String[] { "function" }); paramNames.put("execAsMapEntry", new String[] { "function" }); paramNames.put("execIfNotNullAsSet", new String[] { "function" }); paramNames.put("execAsSet", new String[] { "function" }); paramNames.put("execIfNotNullAsArrayOfArray", new String[] { "function" }); paramNames.put("execAsArrayOfArray", new String[] { "function" }); paramNames.put("execIfNotNullAsArrayOfList", new String[] { "function" }); paramNames.put("execAsArrayOfList", new String[] { "function" }); paramNames.put("execIfNotNullAsArrayOfMap", new String[] { "function" }); paramNames.put("execAsArrayOfMap", new String[] { "function" }); paramNames.put("execIfNotNullAsArrayOfSet", new String[] { "function" }); paramNames.put("execAsArrayOfSet", new String[] { "function" }); paramNames.put("execIfNotNullAsListOfArray", new String[] { "function" }); paramNames.put("execAsListOfArray", new String[] { "function" }); paramNames.put("execIfNotNullAsListOfList", new String[] { "function" }); paramNames.put("execAsListOfList", new String[] { "function" }); paramNames.put("execIfNotNullAsListOfMap", new String[] { "function" }); paramNames.put("execAsListOfMap", new String[] { "function" }); paramNames.put("execIfNotNullAsListOfSet", new String[] { "function" }); paramNames.put("execAsListOfSet", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfArray", new String[] { "function" }); paramNames.put("execAsMapOfArray", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfList", new String[] { "function" }); paramNames.put("execAsMapOfList", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfMap", new String[] { "function" }); paramNames.put("execAsMapOfMap", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfSet", new String[] { "function" }); paramNames.put("execAsMapOfSet", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfArrayEntry", new String[] { "function" }); paramNames.put("execAsMapOfArrayEntry", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfListEntry", new String[] { "function" }); paramNames.put("execAsMapOfListEntry", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfMapEntry", new String[] { "function" }); paramNames.put("execAsMapOfMapEntry", new String[] { "function" }); paramNames.put("execIfNotNullAsMapOfSetEntry", new String[] { "function" }); paramNames.put("execAsMapOfSetEntry", new String[] { "function" }); paramNames.put("execIfNotNullAsSetOfArray", new String[] { "function" }); paramNames.put("execAsSetOfArray", new String[] { "function" }); paramNames.put("execIfNotNullAsSetOfList", new String[] { "function" }); paramNames.put("execAsSetOfList", new String[] { "function" }); paramNames.put("execIfNotNullAsSetOfMap", new String[] { "function" }); paramNames.put("execAsSetOfMap", new String[] { "function" }); paramNames.put("execIfNotNullAsSetOfSet", new String[] { "function" }); paramNames.put("execAsSetOfSet", new String[] { "function" }); paramNames.put("sort", new String[] { "comparator" }); paramNames.put("sortBy", new String[] { "by" }); paramNames.put("put", new String[] { "newKey", "newValue" }); paramNames.put("putAll", new String[] { "map" }); paramNames.put("getAsArrayOf", new String[] { "type" }); paramNames.put("forEach", new String[] { "elementType" }); paramNames.put("replaceWith", new String[] { "replacement" }); paramNames.put("map", new String[] { "function" }); paramNames.put("mapIfNotNull", new String[] { "function" }); varargsPositions = new HashSet<String>(); varargsPositions.add("removeAllIndexes$0"); varargsPositions.add("removeAllIndexesNot$0"); varargsPositions.add("removeAllEqual$0"); varargsPositions.add("removeAllKeys$0"); varargsPositions.add("removeAllKeysNot$0"); varargsPositions.add("addAll$0"); varargsPositions.add("insertAll$1"); varargsPositions.add("ifIndex$0"); varargsPositions.add("ifIndexNot$0"); varargsPositions.add("ifKeyEquals$0"); varargsPositions.add("ifKeyNotEquals$0"); arrayTypeRequired = new HashSet<String>(); arrayTypeRequired.add("Level0ArrayOperator"); arrayTypeRequired.add("Level0ArraySelectedOperator"); arrayTypeRequired.add("Level1ArrayOperator"); arrayTypeRequired.add("Level1ArrayElements"); arrayTypeRequired.add("Level1ArraySelected"); arrayTypeRequired.add("Level0MapOfArray"); arrayTypeRequired.add("Level1MapOfArray"); arrayTypeRequired.add("Level2MapOfArray"); arrayTypeRequired.add("Level3MapOfArray"); currentLevelsByPrefix = new HashMap<String, LevelStructure>(); currentLevelsByPrefix.put("Level0ArrayOperator", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level0ArraySelected", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level0ListOperator", LevelStructure.LIST); currentLevelsByPrefix.put("Level0ListSelected", LevelStructure.LIST); currentLevelsByPrefix.put("Level0MapOperator", LevelStructure.MAP); currentLevelsByPrefix.put("Level0MapSelected", LevelStructure.MAP); currentLevelsByPrefix.put("Level0SetOperator", LevelStructure.SET); currentLevelsByPrefix.put("Level0SetSelected", LevelStructure.SET); currentLevelsByPrefix.put("Level1ArrayElements", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level1ArraySelected", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level1ListElements", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level1ListSelected", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level1MapEntries", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level1MapSelected", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level1SetElements", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level1SetSelected", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapEntries", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapSelected", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ArrayOfArray", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level1ArrayOfArray", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level2ArrayOfArray", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ArrayOfList", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level1ArrayOfList", LevelStructure.LIST); currentLevelsByPrefix.put("Level2ArrayOfList", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ArrayOfMap", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level1ArrayOfMap", LevelStructure.MAP); currentLevelsByPrefix.put("Level2ArrayOfMap", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level3ArrayOfMap", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ArrayOfSet", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level1ArrayOfSet", LevelStructure.SET); currentLevelsByPrefix.put("Level2ArrayOfSet", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ListOfArray", LevelStructure.LIST); currentLevelsByPrefix.put("Level1ListOfArray", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level2ListOfArray", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ListOfList", LevelStructure.LIST); currentLevelsByPrefix.put("Level1ListOfList", LevelStructure.LIST); currentLevelsByPrefix.put("Level2ListOfList", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ListOfMap", LevelStructure.LIST); currentLevelsByPrefix.put("Level1ListOfMap", LevelStructure.MAP); currentLevelsByPrefix.put("Level2ListOfMap", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level3ListOfMap", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0ListOfSet", LevelStructure.LIST); currentLevelsByPrefix.put("Level1ListOfSet", LevelStructure.SET); currentLevelsByPrefix.put("Level2ListOfSet", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0MapOfArray", LevelStructure.MAP); currentLevelsByPrefix.put("Level1MapOfArray", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level2MapOfArraySelectedEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfArrayEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfArrayEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfArraySelectedEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfArraySelectedEntriesValue", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level2MapOfArrayEntriesSelectedValue", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level2MapOfArrayEntriesValue", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level2MapOfArraySelectedEntriesSelectedValue", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level3MapOfArray", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0MapOfList", LevelStructure.MAP); currentLevelsByPrefix.put("Level1MapOfList", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level2MapOfListSelectedEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfListEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfListEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfListSelectedEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfListSelectedEntriesValue", LevelStructure.LIST); currentLevelsByPrefix.put("Level2MapOfListEntriesSelectedValue", LevelStructure.LIST); currentLevelsByPrefix.put("Level2MapOfListEntriesValue", LevelStructure.LIST); currentLevelsByPrefix.put("Level2MapOfListSelectedEntriesSelectedValue", LevelStructure.LIST); currentLevelsByPrefix.put("Level3MapOfList", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0MapOfMap", LevelStructure.MAP); currentLevelsByPrefix.put("Level1MapOfMap", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level2MapOfMapSelectedEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfMapEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfMapEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfMapSelectedEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfMapSelectedEntriesValue", LevelStructure.MAP); currentLevelsByPrefix.put("Level2MapOfMapEntriesSelectedValue", LevelStructure.MAP); currentLevelsByPrefix.put("Level2MapOfMapEntriesValue", LevelStructure.MAP); currentLevelsByPrefix.put("Level2MapOfMapSelectedEntriesSelectedValue", LevelStructure.MAP); currentLevelsByPrefix.put("Level3MapOfMap", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level4MapOfMap", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0MapOfSet", LevelStructure.MAP); currentLevelsByPrefix.put("Level1MapOfSet", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level2MapOfSetSelectedEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfSetEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfSetEntriesKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfSetSelectedEntriesSelectedKey", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level2MapOfSetSelectedEntriesValue", LevelStructure.SET); currentLevelsByPrefix.put("Level2MapOfSetEntriesSelectedValue", LevelStructure.SET); currentLevelsByPrefix.put("Level2MapOfSetEntriesValue", LevelStructure.SET); currentLevelsByPrefix.put("Level2MapOfSetSelectedEntriesSelectedValue", LevelStructure.SET); currentLevelsByPrefix.put("Level3MapOfSet", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0SetOfArray", LevelStructure.SET); currentLevelsByPrefix.put("Level1SetOfArray", LevelStructure.ARRAY); currentLevelsByPrefix.put("Level2SetOfArray", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0SetOfList", LevelStructure.SET); currentLevelsByPrefix.put("Level1SetOfList", LevelStructure.LIST); currentLevelsByPrefix.put("Level2SetOfList", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0SetOfMap", LevelStructure.SET); currentLevelsByPrefix.put("Level1SetOfMap", LevelStructure.MAP); currentLevelsByPrefix.put("Level2SetOfMap", LevelStructure.MAP_ENTRY); currentLevelsByPrefix.put("Level3SetOfMap", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0SetOfSet", LevelStructure.SET); currentLevelsByPrefix.put("Level1SetOfSet", LevelStructure.SET); currentLevelsByPrefix.put("Level2SetOfSet", LevelStructure.ELEMENTS); currentLevelsByPrefix.put("Level0GenericMulti", LevelStructure.LIST); currentLevelsByPrefix.put("Level0GenericUniq", LevelStructure.ELEMENTS); previousLevelsByPrefix = new HashMap<String, LevelStructure>(); previousLevelsByPrefix.put("Level0ArrayOperator", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0ArraySelected", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0ListOperator", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0ListSelected", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0MapOperator", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0MapSelected", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0SetOperator", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0SetSelected", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ArrayElements", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level1ArraySelected", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level1ListElements", LevelStructure.LIST); previousLevelsByPrefix.put("Level1ListSelected", LevelStructure.LIST); previousLevelsByPrefix.put("Level1MapEntries", LevelStructure.MAP); previousLevelsByPrefix.put("Level1MapSelected", LevelStructure.MAP); previousLevelsByPrefix.put("Level1SetElements", LevelStructure.SET); previousLevelsByPrefix.put("Level1SetSelected", LevelStructure.SET); previousLevelsByPrefix.put("Level2MapEntries", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level2MapSelected", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level0ArrayOfArray", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ArrayOfArray", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level2ArrayOfArray", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level0ArrayOfList", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ArrayOfList", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level2ArrayOfList", LevelStructure.LIST); previousLevelsByPrefix.put("Level0ArrayOfMap", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ArrayOfMap", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level2ArrayOfMap", LevelStructure.MAP); previousLevelsByPrefix.put("Level3ArrayOfMap", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level0ArrayOfSet", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ArrayOfSet", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level2ArrayOfSet", LevelStructure.SET); previousLevelsByPrefix.put("Level0ListOfArray", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ListOfArray", LevelStructure.LIST); previousLevelsByPrefix.put("Level2ListOfArray", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level0ListOfList", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ListOfList", LevelStructure.LIST); previousLevelsByPrefix.put("Level2ListOfList", LevelStructure.LIST); previousLevelsByPrefix.put("Level0ListOfMap", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ListOfMap", LevelStructure.LIST); previousLevelsByPrefix.put("Level2ListOfMap", LevelStructure.MAP); previousLevelsByPrefix.put("Level3ListOfMap", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level0ListOfSet", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1ListOfSet", LevelStructure.LIST); previousLevelsByPrefix.put("Level2ListOfSet", LevelStructure.SET); previousLevelsByPrefix.put("Level0MapOfArray", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1MapOfArray", LevelStructure.MAP); previousLevelsByPrefix.put("Level2MapOfArray", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level3MapOfArray", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level0MapOfList", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1MapOfList", LevelStructure.MAP); previousLevelsByPrefix.put("Level2MapOfList", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level3MapOfList", LevelStructure.LIST); previousLevelsByPrefix.put("Level0MapOfMap", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1MapOfMap", LevelStructure.MAP); previousLevelsByPrefix.put("Level2MapOfMap", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level3MapOfMap", LevelStructure.MAP); previousLevelsByPrefix.put("Level4MapOfMap", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level0MapOfSet", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1MapOfSet", LevelStructure.MAP); previousLevelsByPrefix.put("Level2MapOfSet", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level3MapOfSet", LevelStructure.SET); previousLevelsByPrefix.put("Level0SetOfArray", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1SetOfArray", LevelStructure.SET); previousLevelsByPrefix.put("Level2SetOfArray", LevelStructure.ARRAY); previousLevelsByPrefix.put("Level0SetOfList", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1SetOfList", LevelStructure.SET); previousLevelsByPrefix.put("Level2SetOfList", LevelStructure.LIST); previousLevelsByPrefix.put("Level0SetOfMap", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1SetOfMap", LevelStructure.SET); previousLevelsByPrefix.put("Level2SetOfMap", LevelStructure.MAP); previousLevelsByPrefix.put("Level3SetOfMap", LevelStructure.MAP_ENTRY); previousLevelsByPrefix.put("Level0SetOfSet", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level1SetOfSet", LevelStructure.SET); previousLevelsByPrefix.put("Level2SetOfSet", LevelStructure.SET); previousLevelsByPrefix.put("Level0GenericMulti", LevelStructure.LEVEL_DOES_NOT_EXIST); previousLevelsByPrefix.put("Level0GenericUniq", LevelStructure.LEVEL_DOES_NOT_EXIST); } public ImplFile(final ImplType implType, final Class<?> interfaceClass) { super(); try { this.implType = implType; this.imports = new LinkedHashSet<String>(); this.methodImplementations = new ArrayList<String>(); this.methodNames = new LinkedHashSet<String>(); this.interfaceTypeRep = new TypeRep(interfaceClass); this.packageName = interfaceClass.getPackage().getName().replace(".intf.", (implType == ImplType.OP ? ".impl.op." : ".impl.fn.")); this.imports.add(interfaceClass.getName()); this.className = StringUtils.substringBefore(this.interfaceTypeRep.getStringRep(), "<").substring(1) + "<" + StringUtils.substringAfter(this.interfaceTypeRep.getStringRep(), "<"); computeMethodImplementations(implType, interfaceClass); } catch (final Exception e) { throw new RuntimeException(e); } } public LevelStructure getCurrentLevelStructure() { for (final Map.Entry<String, LevelStructure> currentLevel : currentLevelsByPrefix.entrySet()) { if (this.className.startsWith(currentLevel.getKey())) { return currentLevel.getValue(); } } throw new RuntimeException("No current level structure for: " + this.className); } public LevelStructure getPreviousLevelStructure() { for (final Map.Entry<String, LevelStructure> currentLevel : previousLevelsByPrefix.entrySet()) { if (this.className.startsWith(currentLevel.getKey())) { return currentLevel.getValue(); } } throw new RuntimeException("No previous level structure for: " + this.className); } public int getCurrentLevel() { return Integer.parseInt(this.className.substring(5, 6)); } public String getElement() { return this.element; } public boolean hasEndIf() { return this.methodNames.contains("endIf"); } public boolean hasEndOn() { return this.methodNames.contains("endOn"); } public String getPackageName() { return this.packageName; } public String getClassName() { return this.className; } public String getCurrentLevelType() { return this.currentLevelType; } public String getCurrentLevelElement() { return this.currentLevelElement; } public ImplType getImplType() { return this.implType; } public void computeMethodImplementations(final ImplType implType, final Class<?> interfaceClass) throws Exception { final List<Method> interfaceMethods = new ArrayList<Method>(); interfaceMethods.addAll(Arrays.asList(interfaceClass.getDeclaredMethods())); final Set<Type> extendedInterfaces = new HashSet<Type>( Arrays.asList(interfaceClass.getGenericInterfaces())); Type getReturnType = null; for (final Type extendedInterface : extendedInterfaces) { if (extendedInterface instanceof ParameterizedType) { final ParameterizedType pType = (ParameterizedType) extendedInterface; if (((Class<?>) pType.getRawType()).equals(UniqOperator.class)) { getReturnType = pType.getActualTypeArguments()[0]; } } } try { interfaceMethods.add(UniqOpOperator.class.getMethod("get")); } catch (NoSuchMethodException e) { // nothing to do } if (this.className.contains("Array")) { this.element = "T[]"; } else if (this.className.contains("List")) { this.element = "List<T>"; } else if (this.className.contains("Set")) { this.element = "Set<T>"; } else if (this.className.contains("Map")) { this.element = "Map<K,V>"; } else { this.element = "T"; } for (final Method interfaceMethod : interfaceMethods) { final String methodName = interfaceMethod.getName(); this.methodNames.add(methodName); final Type[] parameterTypes = interfaceMethod.getGenericParameterTypes(); if (methodName.startsWith("exec")) { this.currentLevelType = (new TypeRep( ((WildcardType) ((ParameterizedType) parameterTypes[0]).getActualTypeArguments()[0]) .getLowerBounds()[0])).getStringRep(); if (this.currentLevelType.endsWith("[]")) { this.currentLevelElement = this.currentLevelType.substring(0, this.currentLevelType.length() - 2); } else if (this.currentLevelType.startsWith("List<") && this.currentLevelType.endsWith(">")) { this.currentLevelElement = this.currentLevelType.substring(5, this.currentLevelType.length() - 1); } else if (this.currentLevelType.startsWith("Set<") && this.currentLevelType.endsWith(">")) { this.currentLevelElement = this.currentLevelType.substring(4, this.currentLevelType.length() - 1); } else if (this.currentLevelType.startsWith("Map<") && this.currentLevelType.endsWith(">")) { this.currentLevelElement = "Map.Entry<" + this.currentLevelType.substring(4, this.currentLevelType.length() - 1) + ">"; } else { this.currentLevelElement = "%%CURRENTELEMENTSHOULDNOTBEHERE%%"; } } final String returnTypeStr = (methodName.equals("get") ? (implType == ImplType.OP ? this.element : "Function<I," + this.element + ">") : (methodName.equals("getAsArrayOf") ? (implType == ImplType.OP ? this.element + "[]" : "Function<I," + this.element + "[]>") : (methodName.equals("getAsList") ? (implType == ImplType.OP ? "List<" + this.element + ">" : "Function<I,List<" + this.element + ">>") : new TypeRep(interfaceMethod.getGenericReturnType()).getStringRep() .replaceAll("ILevel", "Level")))); final StringBuilder parameterStrBuilder = new StringBuilder(); parameterStrBuilder.append("("); List<String> normalisedParamTypes = new ArrayList<String>(); List<String> normalisedRawParamTypes = new ArrayList<String>(); for (int j = 0; j < parameterTypes.length; j++) { normalisedParamTypes.add(getNormalisedParamType(parameterTypes[j], methodName, j)); normalisedRawParamTypes.add( StringUtils.substringBefore(getNormalisedParamType(parameterTypes[j], methodName, j), "<")); } String[] paramNamesForMethod = paramNames .get(methodName + "%" + StringUtils.join(normalisedRawParamTypes, ",")); if (paramNamesForMethod == null) { paramNamesForMethod = paramNames.get(methodName + "%" + parameterTypes.length); if (paramNamesForMethod == null) { paramNamesForMethod = paramNames.get(methodName); } } for (int j = 0; j < normalisedParamTypes.size(); j++) { if (j > 0) { parameterStrBuilder.append(", "); } parameterStrBuilder.append("final " + normalisedParamTypes.get(j) + " "); if (paramNamesForMethod == null) { throw new RuntimeException("No name for parameter " + j + " of method " + methodName + " in interface " + this.interfaceTypeRep.getStringRep()); } parameterStrBuilder.append(paramNamesForMethod[j]); } parameterStrBuilder.append(")"); final StringBuilder strBuilder = new StringBuilder(); strBuilder.append( " public " + returnTypeStr + " " + methodName + parameterStrBuilder.toString() + " {\n"); strBuilder.append(" return null;\n"); strBuilder.append(" }\n"); this.methodImplementations.add(strBuilder.toString()); } } private String getNormalisedParamType(Type parameterType, String methodName, int index) { final TypeRep paramTypeRep = new TypeRep(parameterType); String paramType = paramTypeRep.getStringRep(); if (paramType.endsWith("[]")) { if (varargsPositions.contains(methodName + "$" + index)) { paramType = StringUtils.substringBeforeLast(paramType, "[]") + "..."; } } return paramType; } public String computeFileContents() { final StringBuilder strBuilder = new StringBuilder(); strBuilder.append(LICENSE_HEADER); strBuilder.append("package " + this.packageName + ";\n\n"); if (isArrayTypeRequired()) { this.imports.add(org.javaruntype.type.Type.class.getName()); } this.imports.add(Target.class.getName()); this.imports.add(NormalisationUtils.class.getName()); this.imports.add(Function.class.getName()); this.imports.add(Normalisation.class.getName().replace("$", ".")); switch (getCurrentLevelStructure()) { case ARRAY: this.imports.add(FnArray.class.getName()); break; case LIST: this.imports.add(FnList.class.getName()); break; case MAP: this.imports.add(FnMap.class.getName()); break; case SET: this.imports.add(FnSet.class.getName()); break; default: } if (this.methodNames.contains("forEach") || this.methodNames.contains("forEachEntry")) { this.imports.add(Structure.class.getName().replace("$", ".")); if (getCurrentLevelStructure().equals(LevelStructure.ARRAY) && getPreviousLevelStructure().equals(LevelStructure.ARRAY)) { this.imports.add(Array.class.getName()); } } this.imports.add(AbstractOperator.class.getName()); this.imports.add(UniqOpOperator.class.getName()); this.imports.add(UniqFnOperator.class.getName()); this.imports.add(List.class.getName()); this.imports.add(Map.class.getName()); this.imports.add(Set.class.getName()); final List<String> importList = new ArrayList<String>(this.imports); Collections.sort(importList); for (final String classImport : importList) { if (classImport.contains(".") && !(classImport.startsWith("java.lang.") && !classImport.startsWith("java.lang.reflect."))) { strBuilder.append("import " + classImport + ";\n"); } } strBuilder.append("\n\n"); if (this.className.contains("GenericMulti")) { final String operatorInterface = (this.implType == ImplType.OP ? "MultiOpOperator" : "MultiFnOperator"); strBuilder.append("public final class " + this.className + " extends AbstractOperator implements " + operatorInterface + "<I," + this.element + ">, " + this.interfaceTypeRep.getStringRep() + " {\n"); } else { final String operatorInterface = (this.implType == ImplType.OP ? "UniqOpOperator" : "UniqFnOperator"); strBuilder.append("public final class " + this.className + " extends AbstractOperator implements " + operatorInterface + "<I," + this.element + ">, " + this.interfaceTypeRep.getStringRep() + " {\n"); } if (isArrayTypeRequired()) { strBuilder.append("\n\n"); final String arrayLetter = (this.className.contains("MapOfArray") ? "V" : "T"); strBuilder.append(" private final Type<" + arrayLetter + "> type;\n"); } strBuilder.append("\n\n"); if (isArrayTypeRequired()) { final String arrayLetter = (this.className.contains("MapOfArray") ? "V" : "T"); strBuilder.append(" public " + StringUtils.substringBefore(this.className, "<") + "(final Type<" + arrayLetter + "> type, final Target target) {\n"); strBuilder.append(" super(target);\n"); strBuilder.append(" this.type = type;\n"); } else { strBuilder.append( " public " + StringUtils.substringBefore(this.className, "<") + "(final Target target) {\n"); strBuilder.append(" super(target);\n"); } strBuilder.append(" }\n"); strBuilder.append("\n\n"); for (final String methodImplementation : this.methodImplementations) { strBuilder.append(methodImplementation + "\n\n"); } strBuilder.append("\n}\n"); String fileContents = strBuilder.toString(); if (isArrayTypeRequired()) { fileContents = MethodImplementor.processArray(implType, fileContents, getCurrentLevel(), getCurrentLevelStructure(), getPreviousLevelStructure(), getCurrentLevelType(), getCurrentLevelElement(), hasEndIf(), hasEndOn()); } else { fileContents = MethodImplementor.processNonArray(implType, fileContents, getCurrentLevel(), getCurrentLevelStructure(), getPreviousLevelStructure(), getCurrentLevelType(), getCurrentLevelElement(), hasEndIf(), hasEndOn()); } return fileContents; } private boolean isArrayTypeRequired() { for (final String prefix : arrayTypeRequired) { if (this.className.contains(prefix)) { return true; } } return false; } public class TypeRep { private final String stringRep; private final Class<?> componentClass; public TypeRep(final Type type) { super(); if (type instanceof GenericArrayType) { final GenericArrayType gatType = (GenericArrayType) type; final TypeRep component = new TypeRep(gatType.getGenericComponentType()); this.stringRep = component.getStringRep() + "[]"; this.componentClass = component.getComponentClass(); } else if (type instanceof ParameterizedType) { final ParameterizedType pType = (ParameterizedType) type; final TypeRep componentType = new TypeRep(pType.getRawType()); this.componentClass = componentType.getComponentClass(); final String[] paramStringReps = new String[pType.getActualTypeArguments().length]; for (int i = 0; i < pType.getActualTypeArguments().length; i++) { final TypeRep paramTypeRep = new TypeRep(pType.getActualTypeArguments()[i]); paramStringReps[i] = paramTypeRep.getStringRep(); } final StringBuilder srStrBuilder = new StringBuilder(); srStrBuilder.append(this.componentClass.getSimpleName()); srStrBuilder.append("<"); srStrBuilder.append(StringUtils.join(paramStringReps, ",")); srStrBuilder.append(">"); this.stringRep = srStrBuilder.toString(); } else if (type instanceof TypeVariable<?>) { final TypeVariable<?> tvType = (TypeVariable<?>) type; this.stringRep = tvType.getName(); this.componentClass = null; } else if (type instanceof WildcardType) { final WildcardType wType = (WildcardType) type; if (wType.getLowerBounds() != null && wType.getLowerBounds().length > 0) { this.stringRep = "? super " + (new TypeRep(wType.getLowerBounds()[0]).getStringRep()); } else if (wType.getUpperBounds() != null && wType.getUpperBounds().length > 0) { this.stringRep = "? extends " + (new TypeRep(wType.getUpperBounds()[0]).getStringRep()); } else { this.stringRep = "?"; } this.componentClass = null; } else { // type instanceof Class<?> final Class<?> cType = (Class<?>) type; this.componentClass = cType; ImplFile.this.imports.add(cType.getName().replaceAll("\\$", ".")); final StringBuilder srStrBuilder = new StringBuilder(); srStrBuilder.append(cType.getSimpleName()); if (cType.getTypeParameters() != null && cType.getTypeParameters().length > 0) { final String[] paramStringReps = new String[cType.getTypeParameters().length]; for (int i = 0; i < cType.getTypeParameters().length; i++) { final TypeRep paramTypeRep = new TypeRep(cType.getTypeParameters()[i]); paramStringReps[i] = paramTypeRep.getStringRep(); } srStrBuilder.append("<"); srStrBuilder.append(StringUtils.join(paramStringReps, ",")); srStrBuilder.append(">"); } this.stringRep = srStrBuilder.toString(); } } public String getStringRep() { return this.stringRep; } public Class<?> getComponentClass() { return this.componentClass; } } }