Java tutorial
/* * Copyright 2012 SAP AG * * Licensed 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 com.sap.research.connectivity.gw; import static org.springframework.roo.model.RooJavaType.ROO_JAVA_BEAN; import static org.springframework.roo.model.RooJavaType.ROO_JPA_ACTIVE_RECORD; import static org.springframework.roo.model.RooJavaType.ROO_TO_STRING; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.SortedSet; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.springframework.roo.classpath.TypeLocationService; import org.springframework.roo.classpath.TypeManagementService; import org.springframework.roo.classpath.converters.JavaTypeConverter; import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder; import org.springframework.roo.classpath.operations.Cardinality; import org.springframework.roo.classpath.operations.Fetch; import org.springframework.roo.classpath.operations.FieldCommands; import org.springframework.roo.file.monitor.event.FileDetails; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.process.manager.FileManager; import org.springframework.roo.process.manager.MutableFile; import org.springframework.roo.project.Dependency; import org.springframework.roo.project.Path; import org.springframework.roo.project.PathResolver; import org.springframework.roo.project.ProjectOperations; import org.springframework.roo.support.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.sap.research.connectivity.gw.parsers.JavaSourceField; import com.sap.research.connectivity.gw.parsers.JavaSourceFieldBuilder; import com.sap.research.connectivity.gw.parsers.JavaSourceFileEditor; import com.sap.research.connectivity.gw.parsers.JavaSourceMethod; import com.sap.research.connectivity.gw.parsers.JavaSourceMethodBuilder; import com.sap.research.connectivity.gw.parsers.MetadataXMLParser; @Component public class GWOperationsUtils { /** * Get hold of a JDK Logger */ protected Logger log = Logger.getLogger(getClass().getName()); protected static final char SEPARATOR = GwUtils.SEPARATOR; /** * Get a reference to the FileManager from the underlying OSGi container. Make sure you * are referencing the Roo bundle which contains this service in your add-on pom.xml. * * Using the Roo file manager instead if java.io.File gives you automatic rollback in case * an Exception is thrown. */ @Reference protected FileManager fileManager; /** * Get a reference to the ProjectOperations from the underlying OSGi container. Make sure you * are referencing the Roo bundle which contains this service in your add-on pom.xml. */ @Reference protected ProjectOperations projectOperations; /** * Use TypeLocationService to find types which are annotated with a given annotation in the project */ @Reference protected TypeLocationService typeLocationService; @Reference protected PathResolver pathResolver; @Reference protected TypeManagementService typeManagementService; /* ------------------------------------------------------------------------------------------------------------------ * SAP RESEARCH OWN CODE * * */ /* * Used for sub-package path of the connectivity classes */ public static final String oDataFolder = "connectivity"; public static final String domain = "domain"; public static final String web = "web"; protected static final AnnotationMetadataBuilder ROO_JAVA_BEAN_BUILDER = new AnnotationMetadataBuilder( ROO_JAVA_BEAN); protected static final AnnotationMetadataBuilder ROO_TO_STRING_BUILDER = new AnnotationMetadataBuilder( ROO_TO_STRING); protected static final AnnotationMetadataBuilder ROO_JPA_ACTIVE_RECORD_BUILDER = new AnnotationMetadataBuilder( ROO_JPA_ACTIVE_RECORD); protected static final String PERSISTENCE_XML = "META-INF/persistence.xml"; private static final String ENCODED_KEY = "encodedKey"; private static final String DECODED_KEY = "decodedKey"; private static final String ODATA_KEY = "ODataKey"; public void addODataDependenciesToPom() { List<Dependency> dependencies = new ArrayList<Dependency>(); // Install dependencies defined in external XML file for (Element dependencyElement : XmlUtils.findElements("/configuration/batch/dependencies/dependency", XmlUtils.getConfiguration(getClass()))) { dependencies.add(new Dependency(dependencyElement)); } // Add all new dependencies to pom.xml projectOperations.addDependencies("", dependencies); } public void addOdataConnectivityClass() { Map<String, String> replacements = new HashMap<String, String>(); replacements.put("<<PACKAGE>>", "package " + getTopLevelPackageName() + "." + oDataFolder + ";\n"); GwUtils.createClassFileFromTemplate(getTopLevelPackageName(), getSubPackagePath(oDataFolder), "ODataConnectivity_template.java", "ODataConnectivity.java", replacements, fileManager, getClass()); } /* * This method should dump all the metadata existing at the specified url. * It calls an external standalone java app, which should be present in user home (regardless of OS) */ public String getMetadataString(String url, String user, String pass, String host, String port, int timeOut) throws Exception { String returnString = ""; try { String execArgs[] = new String[] { "java", "-jar", System.getProperty("user.home") + SEPARATOR + "appToRetrieveOdataMetadata.jar", url, user, pass, host, port }; final Process theProcess = Runtime.getRuntime().exec(execArgs); Callable<String> call = new Callable<String>() { public String call() throws Exception { String returnString = ""; try { BufferedReader inStream = new BufferedReader( new InputStreamReader(theProcess.getInputStream())); returnString = IOUtils.toString(inStream); IOUtils.closeQuietly(inStream); //if (theProcess.exitValue() != 0) theProcess.waitFor(); } catch (InterruptedException e) { throw new TimeoutException(); //log.severe("The call to the Gateway Service was interrupted."); } return returnString; } }; final ExecutorService theExecutor = Executors.newSingleThreadExecutor(); Future<String> futureResultOfCall = theExecutor.submit(call); try { returnString = futureResultOfCall.get(timeOut, TimeUnit.SECONDS); } catch (TimeoutException ex) { throw new TimeoutException( "The Gateway Service call timed out. Please try again or check your settings."); } catch (ExecutionException ex) { throw new RuntimeException("The Gateway Service call did not complete due to an execution error. " + ex.getCause().getLocalizedMessage()); } finally { theExecutor.shutdownNow(); } } catch (InterruptedException ex) { throw new InterruptedException( "The Gateway Service call did not complete due to an unexpected interruption."); } catch (IOException e) { throw new IOException("Error when retrieving metadata from the Gateway Service."); } return returnString; } public String getTopLevelPackageName() { return projectOperations.getFocusedTopLevelPackage().getFullyQualifiedPackageName(); } public String getSubPackagePath(String subPackageName) { String packagePath = getTopLevelPackageName().replace('.', SEPARATOR); PathResolver pathResolver = projectOperations.getPathResolver(); String path = pathResolver.getFocusedIdentifier(Path.SRC_MAIN_JAVA, packagePath + SEPARATOR + subPackageName); return path; } public JavaSourceFileEditor getJavaFileEditor(final String subPackagePathName, final String className) { return getJavaFileEditor(subPackagePathName, className, true); } public JavaSourceFileEditor getJavaFileEditor(final String subPackagePathName, final String className, final boolean createIfNotExists) { String entityClassPath = getSubPackagePath(subPackagePathName) + SEPARATOR + className + ".java"; MutableFile sourceEntityFile = null; JavaSourceFileEditor entityClassFile = null; if (fileManager.exists(entityClassPath)) { sourceEntityFile = fileManager.updateFile(entityClassPath); entityClassFile = new JavaSourceFileEditor(sourceEntityFile); } else if (createIfNotExists) { sourceEntityFile = fileManager.createFile(entityClassPath); entityClassFile = new JavaSourceFileEditor(sourceEntityFile); } return entityClassFile; } public Map<String[], String> getFieldsOfRemoteEntity(String entityClassName, String nameSpace) { Map<String[], String> fields = new HashMap<String[], String>(); String metaDataPath = getSubPackagePath(oDataFolder); String metaDataFile = metaDataPath + SEPARATOR + nameSpace + "_metadata.xml"; InputStream metaDataIs = fileManager.getInputStream(metaDataFile); Document doc; MetadataXMLParser xmlParser; try { doc = XmlUtils.getDocumentBuilder().parse(metaDataIs); xmlParser = new MetadataXMLParser(doc, entityClassName); xmlParser.parse(); } catch (Exception ex) { throw new IllegalStateException(ex); } fields = xmlParser.getFields(); return fields; } public void addRemoteFieldInPersistenceMethods(JavaSourceFileEditor entityClassFile, Map.Entry<String[], String> fieldObj) { ArrayList<JavaSourceMethod> globalMethodList = entityClassFile.getGlobalMethodList(); String pluralRemoteEntity = GwUtils.getInflectorPlural(entityClassFile.CLASS_NAME, Locale.ENGLISH); String smallRemoteEntity = StringUtils.uncapitalize(entityClassFile.CLASS_NAME); for (JavaSourceMethod method : globalMethodList) { String methodName = method.getMethodName(); /* * We insert the new field in the persist and merge methods */ if (methodName.endsWith("persist") || methodName.endsWith("merge")) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); methodBody.insert(methodBody.lastIndexOf(".execute()"), makeGWPersistFieldCode("", fieldObj)); method.setMethodBody(methodBody.toString()); } /* * We insert the new field in the findAll and find<Entity>Entries methods */ else if (methodName.endsWith("findAll" + pluralRemoteEntity) || methodName.endsWith("find" + entityClassFile.CLASS_NAME + "Entries")) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); methodBody.insert(methodBody.indexOf("virtual" + entityClassFile.CLASS_NAME + "List.add"), makeGWShowFieldCode("", smallRemoteEntity + "Instance", smallRemoteEntity + "Item", fieldObj)); method.setMethodBody(methodBody.toString()); } /* * We insert the new field in the find<Entity> method */ else if (methodName.endsWith("find" + entityClassFile.CLASS_NAME)) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); methodBody.insert(methodBody.indexOf("return "), makeGWShowFieldCode("", "virtual" + entityClassFile.CLASS_NAME, smallRemoteEntity, fieldObj)); method.setMethodBody(methodBody.toString()); } } } public void addLocalFieldInPersistenceMethods(JavaSourceFileEditor entityClassFile, String fieldName, String fieldType) { ArrayList<JavaSourceMethod> globalMethodList = entityClassFile.getGlobalMethodList(); String pluralRemoteEntity = GwUtils.getInflectorPlural(entityClassFile.CLASS_NAME, Locale.ENGLISH); String smallRemoteEntity = StringUtils.uncapitalize(entityClassFile.CLASS_NAME); for (JavaSourceMethod method : globalMethodList) { String methodName = method.getMethodName(); /* * We insert the new field in the findAll and find<Entity>Entries methods */ if (methodName.endsWith("findAll" + pluralRemoteEntity) || methodName.endsWith("find" + entityClassFile.CLASS_NAME + "Entries")) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); methodBody.insert(methodBody.indexOf("virtual" + entityClassFile.CLASS_NAME + "List.add"), makeLocalShowFieldCode("\t\t", smallRemoteEntity + "Instance", entityClassFile.CLASS_NAME, fieldName)); method.setMethodBody(methodBody.toString()); } /* NO NEED TO INSERT IN THE FIND METHOD ANYMORE AS ONCE FOUND IN THE LOCAL DB, THE LOCAL FIELDS ARE AUTOMATICALLY POPULATED * We insert the new field in the find<Entity> method * else if (methodName.endsWith("find" + entityClassFile.CLASS_NAME)) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); methodBody.insert(methodBody.indexOf("return "), makeLocalShowFieldCode("\t", "virtual" + entityClassFile.CLASS_NAME, entityClassFile.CLASS_NAME, fieldName)); method.setMethodBody(methodBody.toString()); }*/ } } public void addPersistenceMethods(Map<String[], String> fields, JavaSourceFileEditor entityClassFile, String remoteEntity, Map<String[], String> keys) { // Persist JavaSourceMethod persistMethod = new JavaSourceMethodBuilder().methodName("persist").methodPrefix("public") .returnType("void").annotations("@Transactional") .methodBody(getPersistMethodBody(fields, keys, remoteEntity)).build(); entityClassFile.addMethod(persistMethod); /* * localPersist - needed to store at least ID's on the local persistence container, as currently needed by local fields, references to other entities, etc. * TODO: keep only the id and local fields in the persisted entity, the rest should be cleared */ JavaSourceMethod localPersistMethod = new JavaSourceMethodBuilder().methodName("localPersist") .methodPrefix("private").returnType("void").annotations("@Transactional") .methodBody(getLocalPersistMethodBody()).build(); entityClassFile.addMethod(localPersistMethod); // findAll String pluralRemoteEntity = GwUtils.getInflectorPlural(remoteEntity, Locale.ENGLISH); JavaSourceMethod findAllMethod = new JavaSourceMethodBuilder().methodName("findAll" + pluralRemoteEntity) .methodPrefix("public static").returnType("List" + "<" + remoteEntity + ">") .methodBody(getfindAllMethodBody(fields, keys, remoteEntity)).build(); entityClassFile.addMethod(findAllMethod); // findEntries JavaSourceMethod findEntries = new JavaSourceMethodBuilder().methodName("find" + remoteEntity + "Entries") .methodPrefix("public static").returnType("List" + "<" + remoteEntity + ">") .parameters(getFindEntriesMethodParameters()) .methodBody(getfindAllMethodBody(fields, keys, remoteEntity)).build(); entityClassFile.addMethod(findEntries); // find JavaSourceMethod find = new JavaSourceMethodBuilder().methodName("find" + remoteEntity) .methodPrefix("public static").returnType(remoteEntity).parameters(getFindMethodParameters()) .methodBody(getFindMethodBody(fields, remoteEntity)).build(); entityClassFile.addMethod(find); // count JavaSourceMethod count = new JavaSourceMethodBuilder().methodName("count" + pluralRemoteEntity) .methodPrefix("public static").returnType("long").methodBody(getCountMethodBody(remoteEntity)) .build(); entityClassFile.addMethod(count); // merge JavaSourceMethod merge = new JavaSourceMethodBuilder().methodName("merge").methodPrefix("public") .returnType(remoteEntity).annotations("@Transactional") .methodBody(getMergeMethodBody(fields, remoteEntity, keys)).build(); entityClassFile.addMethod(merge); /* * localMerge - needed by local fields, references to other entities, etc. */ JavaSourceMethod localMerge = new JavaSourceMethodBuilder().methodName("localMerge").methodPrefix("private") .returnType(remoteEntity).annotations("@Transactional") .methodBody(getLocalMergeMethodBody(remoteEntity)).build(); entityClassFile.addMethod(localMerge); // remove JavaSourceMethod remove = new JavaSourceMethodBuilder().methodName("remove").methodPrefix("public") .returnType("void").annotations("@Transactional").methodBody(getRemoveMethodBody(remoteEntity)) .build(); entityClassFile.addMethod(remove); /* * localMerge - needed by local fields, references to other entities, etc. */ JavaSourceMethod localRemove = new JavaSourceMethodBuilder().methodName("localRemove") .methodPrefix("public").returnType("void").annotations("@Transactional") .methodBody(getLocalRemoveMethodBody(remoteEntity)).build(); entityClassFile.addMethod(localRemove); JavaSourceMethod getRemoteEntity = new JavaSourceMethodBuilder().methodName("getRemote" + remoteEntity) .methodPrefix("public static").returnType("OEntity").parameters(getFindMethodParameters()) .methodBody(getRemoteEntityMethodBody(remoteEntity)).build(); entityClassFile.addMethod(getRemoteEntity); } private String getRemoteEntityMethodBody(String remoteEntity) { String returnString = "\t\tOEntityKey " + ODATA_KEY + " = OEntityKey.parse( " + GwUtils.GW_CONNECTION_FIELD_NAME + ".getDecodedRemoteKey(Id));\n" + "\t\treturn " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.getEntity(\"" + remoteEntity + "\", " + ODATA_KEY + ").execute();\n"; return returnString; } public String getRemoveMethodBody(String remoteEntity) { String returnString = "\t\tOEntityKey " + ODATA_KEY + " = OEntityKey.parse(" + GwUtils.GW_CONNECTION_FIELD_NAME + ".getDecodedRemoteKey(Id));\n"; returnString += "\t\t" + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.deleteEntity(\"" + remoteEntity + "\", ODataKey).execute();\n" + "\t\tlocalRemove();\n"; return returnString; } public String getLocalRemoveMethodBody(String remoteEntity) { String returnString = "\t\tif (this.entityManager == null) this.entityManager = entityManager();\n" + "\t\tif (this.entityManager.contains(this)) {\n" + "\t\t\tthis.entityManager.remove(this);\n" + "\t\t} else {\n" + "\t\t\t" + remoteEntity + " local" + remoteEntity + " = entityManager().find(" + remoteEntity + ".class, Id);\n" + "\t\t\tthis.entityManager.remove(local" + remoteEntity + ");\n" + "\t\t}\n"; return returnString; } public String getMergeMethodBody(Map<String[], String> fields, String remoteEntity, Map<String[], String> keys) { String returnString = "\t\tOEntity remote" + remoteEntity + " = getRemote" + remoteEntity + "(Id);\n"; returnString += "\t\tOModifyRequest<OEntity> modifyEntityRequest = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.updateEntity(remote" + remoteEntity + ");\n"; returnString += "\t\tboolean modifyRequest = modifyEntityRequest\n"; for (Map.Entry<String[], String> field : fields.entrySet()) { /* * We do not update the key fields */ if (!keys.containsKey(field.getKey())) { returnString += makeGWPersistFieldCode("\t\t\t", field); } } returnString += "\t\t\t.execute();\n"; returnString += "\t\t" + remoteEntity + " localMerged = localMerge();\n"; returnString += "\n"; returnString += "\t\treturn localMerged;\n"; return returnString; } public String getLocalMergeMethodBody(String remoteEntity) { String returnString = "\t\tif (this.entityManager == null) this.entityManager = entityManager();\n" + "\t\t" + remoteEntity + " merged = this.entityManager.merge(this);\n" + "\t\tthis.entityManager.flush();\n" + "\t\treturn merged;\n"; return returnString; } public String getCountMethodBody(String remoteEntity) { String smallRemoteEntity = StringUtils.uncapitalize(remoteEntity); String returnString = "\t\tOQueryRequest<OEntity> " + smallRemoteEntity + "List = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.getEntities(\"" + remoteEntity + "\");\n"; returnString += "\t\tint i = 0;\n"; returnString += "\t\tfor (OEntity " + smallRemoteEntity + "Item : " + smallRemoteEntity + "List) {\n"; returnString += "\t\t\ti++;\n"; returnString += "\t\t}\n"; returnString += "\t\treturn i;\n"; return returnString; } public String getFindMethodBody(Map<String[], String> fields, String remoteEntity) { String smallRemoteEntity = StringUtils.uncapitalize(remoteEntity); String returnString = "\t\tOEntity " + smallRemoteEntity + " = getRemote" + remoteEntity + "(Id);\n"; returnString += "\t\t" + remoteEntity + " virtual" + remoteEntity + " = entityManager().find(" + remoteEntity + ".class, " + GwUtils.GW_CONNECTION_FIELD_NAME + ".getDecodedRemoteKey(Id));\n"; returnString += "\t\tif (virtual" + remoteEntity + " == null)\n" + "\t\t\tvirtual" + remoteEntity + " = new " + remoteEntity + "();\n"; returnString += "\t\tDateTimeFormatter DTformatter = ISODateTimeFormat.dateHourMinuteSecondFraction();\n"; returnString += "\t\tDateTimeFormatter DTOformatter = ISODateTimeFormat.dateTime();\n"; for (Map.Entry<String[], String> field : fields.entrySet()) { returnString += makeGWShowFieldCode("\t\t", "virtual" + remoteEntity, smallRemoteEntity, field); } //returnString += "\t\t" + "virtual" + remoteEntity + ".setId(" + DECODED_KEY + ");\n"; //returnString += "\t\t" + remoteEntity + " local" + remoteEntity + " = entityManager().find(" + remoteEntity + ".class, " + DECODED_KEY + ");\n"; returnString += "\t\ttry {\n" + "\t\t\t\n" + "\t\t} catch (Exception relationshipsException) {\n" + "\t\t\trelationshipsException.printStackTrace();\n" + "\t\t};\n"; returnString += "\t\treturn " + "virtual" + remoteEntity + ";\n"; return returnString; } public String getControllerShowMethodBody(String remoteEntity) { String smallRemoteEntity = StringUtils.lowerCase(remoteEntity); String pluralRemoteEntity = GwUtils.getInflectorPlural(remoteEntity, Locale.ENGLISH); String returnString = ""; if (existsDateFieldInController(remoteEntity)) { returnString += "\t\taddDateTimeFormatPatterns(uiModel);\n"; } returnString += "\t\tuiModel.addAttribute(\"" + smallRemoteEntity + "\", " + remoteEntity + ".find" + remoteEntity + "(Id));\n"; returnString += generateURLEncodingCode("Id", "\t\t"); returnString += "\t\tuiModel.addAttribute(\"itemId\", " + ENCODED_KEY + ");\n"; returnString += "\t\treturn \"" + StringUtils.lowerCase(pluralRemoteEntity) + "/show\";\n"; return returnString; } public ArrayList<String> getFindMethodParameters() { ArrayList<String> parameters = new ArrayList<String>(); parameters.add("String Id"); return parameters; } public ArrayList<String> getFindEntriesMethodParameters() { ArrayList<String> parameters = new ArrayList<String>(); parameters.add("int firstResult"); parameters.add("int maxResults"); return parameters; } public ArrayList<String> getControllerShowMethodParameters() { ArrayList<String> parameters = new ArrayList<String>(); parameters.add("@PathVariable(\"Id\") String Id"); parameters.add("Model uiModel"); return parameters; } public String getfindAllMethodBody(Map<String[], String> fields, Map<String[], String> keys, String remoteEntity) { String smallRemoteEntity = StringUtils.uncapitalize(remoteEntity); String returnString = "\t\tOQueryRequest<OEntity> " + smallRemoteEntity + "List = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.getEntities(\"" + remoteEntity + "\");\n"; returnString += "\t\tList<" + remoteEntity + "> virtual" + remoteEntity + "List = " + "new ArrayList<" + remoteEntity + ">();\n"; returnString += "\t\tfor (OEntity " + smallRemoteEntity + "Item : " + smallRemoteEntity + "List) {\n"; returnString += "\t\t\t" + remoteEntity + " " + smallRemoteEntity + "Instance = new " + remoteEntity + "();\n"; returnString += "\t\t\tDateTimeFormatter DTformatter = ISODateTimeFormat.dateHourMinuteSecondFraction();\n"; returnString += "\t\t\tDateTimeFormatter DTOformatter = ISODateTimeFormat.dateTime();\n"; for (Map.Entry<String[], String> field : fields.entrySet()) { returnString += makeGWShowFieldCode("\t\t\t", smallRemoteEntity + "Instance", smallRemoteEntity + "Item", field); } String instance = smallRemoteEntity + "Instance"; returnString += generateEncodedKey(keys, 3, instance); returnString += "\t\t\tString " + DECODED_KEY + " = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".getDecodedRemoteKey(" + ODATA_KEY + ".toKeyString());\n"; /* * Here we deal with the local part (e.g. fields, if any) * (the idea is that if we browse through a remote list of entities, then we might encounter some that have not been created using * the generated backend. So then we must first persist them (at least the id's) */ returnString += "\t\t\t" + remoteEntity + " local" + remoteEntity + " = entityManager().find(" + remoteEntity + ".class, " + DECODED_KEY + ");\n"; returnString += "\t\t\tif (local" + remoteEntity + " == null) {\n" + "\t\t\t\t" + remoteEntity + " tempLocal" + remoteEntity + " = new " + remoteEntity + "();\n" + "\t\t\t\ttempLocal" + remoteEntity + ".entityManager = entityManager();\n" + "\t\t\t\ttempLocal" + remoteEntity + ".setId(" + DECODED_KEY + ");\n" + "\t\t\t\ttempLocal" + remoteEntity + ".localPersist();\n" + "\t\t\t\tlocal" + remoteEntity + " = entityManager().find(" + remoteEntity + ".class, " + DECODED_KEY + ");\n" + "\t\t\t}\n"; returnString += "\t\t\ttry {\n" + "\t\t\t\t\n" + "\t\t\t} catch (Exception relationshipsException) {\n" + "\t\t\t\trelationshipsException.printStackTrace();\n" + "\t\t\t};\n"; returnString += "\t\t\tvirtual" + remoteEntity + "List.add(" + smallRemoteEntity + "Instance);\n"; returnString += "\t\t}\n"; returnString += "\n"; returnString += "\t\treturn " + "virtual" + remoteEntity + "List;\n"; return returnString; } public String getPersistMethodBody(Map<String[], String> fields, Map<String[], String> keys, String remoteEntity) { String returnString = "\t\tOEntity newEntity;\n"; returnString += "\n"; returnString += "\t\tOCreateRequest<OEntity> newEntityRequest = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.createEntity(\"" + remoteEntity + "\");\n"; returnString += "\n"; returnString += "\t\tnewEntity = newEntityRequest\n"; for (Map.Entry<String[], String> field : fields.entrySet()) { returnString += makeGWPersistFieldCode("\t\t\t", field); } returnString += "\t\t\t.execute();\n"; /* * Commented, as when writing to the local persistence db we don't need urlencoding */ //returnString += generateEncodedKey(keys, 2, ""); for (Map.Entry<String[], String> keyField : keys.entrySet()) { returnString += makeGWShowFieldCode("\t\t", "this", "newEntity", keyField); } returnString += generateDBKeyFromODataKeys(keys, "\t\t", ""); returnString += "\t\tsetId(" + ODATA_KEY + ".toKeyString());\n"; returnString += "\t\tlocalPersist();\n"; return returnString; } public String getLocalPersistMethodBody() { String returnString = "\t\tif (this.entityManager == null) this.entityManager = entityManager();\n" + "\t\tthis.entityManager.persist(this);\n"; return returnString; } public String generateEncodedKey(Map<String[], String> keys, int numberOfTabs, String instance) { String returnString = "\n"; String tabs = ""; String instanceMethod = ""; for (int i = 0; i < numberOfTabs; i++) { tabs = tabs + "\t"; } if (!instance.equals("")) instanceMethod = instance + "."; returnString += generateDBKeyFromODataKeys(keys, tabs, instanceMethod); returnString += tabs + instanceMethod + "setId(" + GwUtils.GW_CONNECTION_FIELD_NAME + ".getEncodedRemoteKey(" + ODATA_KEY + ".toKeyString()));\n"; return returnString; } public String generateURLEncodingCode(String variableToEncode, String tabs) { String returnString = tabs + "String " + ENCODED_KEY + " = null;\n"; returnString += tabs + "try {\n"; returnString += tabs + "\t" + ENCODED_KEY + " = URLEncoder.encode(" + variableToEncode + ",\"UTF-8\");\n"; returnString += tabs + "} catch (UnsupportedEncodingException e) {\n"; returnString += tabs + "\te.printStackTrace();\n"; returnString += tabs + "}\n"; return returnString; } private String generateDBKeyFromODataKeys(Map<String[], String> keys, String tabs, String instanceMethod) { String returnString; String keyBuilderString = ""; String separator = ""; for (Map.Entry<String[], String> key : keys.entrySet()) { String remoteFieldName = key.getKey()[0]; String localFieldName = key.getKey()[1]; keyBuilderString += separator + "\"" + remoteFieldName + "\", "; keyBuilderString += instanceMethod + "get" + StringUtils.capitalize(localFieldName) + "()"; separator = ","; } returnString = tabs + "OEntityKey " + ODATA_KEY + " = OEntityKey.create(" + keyBuilderString + ");\n"; return returnString; } public String generateDecodedKey(int numberOfTabs, String keyNameToDecode) { String returnString = "\n"; String tabs = ""; for (int i = 0; i < numberOfTabs; i++) { tabs = tabs + "\t"; } returnString += tabs + "String " + DECODED_KEY + " = \"\";\n"; returnString += tabs + "try{\n"; returnString += tabs + "\t" + DECODED_KEY + " = URLDecoder.decode(" + keyNameToDecode + ",\"UTF-8\");\n"; returnString += tabs + "}catch(UnsupportedEncodingException e){\n"; returnString += tabs + "}\n"; return returnString; } private String makeGWShowFieldCode(String tabLevels, String entityName, String remoteEntity, Map.Entry<String[], String> field) { String startCast = "", endCast = "", returnString = ""; String remoteFieldName = field.getKey()[0]; String localFieldName = field.getKey()[1]; if (field.getValue().equals("DateTime")) { returnString = tabLevels + "DateTime " + localFieldName.toLowerCase() + "DT = DTformatter.parseDateTime(" + remoteEntity + ".getProperty(\"" + remoteFieldName + "\").getValue().toString());\n"; returnString += tabLevels + "Date " + localFieldName.toLowerCase() + "ConvertedDate = " + localFieldName.toLowerCase() + "DT.toDate();\n"; returnString += tabLevels + entityName + ".set" + StringUtils.capitalize(localFieldName) + "(" + localFieldName.toLowerCase() + "ConvertedDate);\n"; } else if (field.getValue().equals("DateTimeOffset")) { returnString = tabLevels + "DateTime " + localFieldName.toLowerCase() + "DT = DTOformatter.parseDateTime(" + remoteEntity + ".getProperty(\"" + remoteFieldName + "\").getValue().toString());\n"; returnString += tabLevels + "Date " + localFieldName.toLowerCase() + "ConvertedDate = " + localFieldName.toLowerCase() + "DT.toDate();\n"; returnString += tabLevels + entityName + ".set" + StringUtils.capitalize(localFieldName) + "(" + localFieldName.toLowerCase() + "ConvertedDate);\n"; } else { String javaType = GwUtils.odataToJavaType(field.getValue()); startCast = GwUtils.generateCast(javaType); if (!startCast.isEmpty()) endCast = ")"; returnString = tabLevels + entityName + ".set" + StringUtils.capitalize(localFieldName) + "(" + startCast + remoteEntity + ".getProperty(\"" + remoteFieldName + "\").getValue().toString()" + endCast + ");\n"; } return returnString; } private String makeLocalShowFieldCode(String tabLevels, String entityName, String remoteEntity, String fieldName) { String returnString = entityName + ".set" + StringUtils.capitalize(fieldName) + "(local" + remoteEntity + "== null ? null : local" + remoteEntity + ".get" + StringUtils.capitalize(fieldName) + "());\n" + tabLevels; return returnString; } public void addImports(JavaSourceFileEditor entityClassFile, String namespace) { ArrayList<String> connectivityImports = new ArrayList<String>(); connectivityImports.add(getTopLevelPackageName() + ".connectivity." + namespace); connectivityImports.add(getTopLevelPackageName() + ".connectivity.ODataConnectivity"); connectivityImports.add("org.odata4j.core.OEntity"); connectivityImports.add("org.odata4j.core.OProperties"); connectivityImports.add("org.springframework.transaction.annotation.Transactional"); connectivityImports.add("java.util.List"); connectivityImports.add("java.util.ArrayList"); connectivityImports.add("java.util.Date"); connectivityImports.add("java.util.Calendar"); connectivityImports.add("org.odata4j.core.OQueryRequest"); connectivityImports.add("org.odata4j.core.OCreateRequest"); connectivityImports.add("org.odata4j.core.OModifyRequest"); connectivityImports.add("org.odata4j.core.OEntityKey"); connectivityImports.add("javax.persistence.Temporal"); connectivityImports.add("javax.persistence.TemporalType"); connectivityImports.add("org.springframework.format.annotation.DateTimeFormat"); connectivityImports.add("org.joda.time.DateTime"); connectivityImports.add("org.joda.time.format.DateTimeFormatter"); connectivityImports.add("org.joda.time.format.ISODateTimeFormat"); connectivityImports.add("org.odata4j.core.OEntityKey"); connectivityImports.add("javax.persistence.Column"); connectivityImports.add("javax.persistence.GenerationType"); connectivityImports.add("javax.persistence.GeneratedValue"); connectivityImports.add("java.net.URLDecoder"); connectivityImports.add("java.net.URLEncoder"); connectivityImports.add("java.io.UnsupportedEncodingException"); connectivityImports.add("javax.persistence.Id"); entityClassFile.addImports(connectivityImports); } public void addControllerImports(JavaSourceFileEditor entityClassFile) { ArrayList<String> connectivityImports = new ArrayList<String>(); connectivityImports.add("org.springframework.web.bind.annotation.PathVariable"); connectivityImports.add("org.springframework.ui.Model"); connectivityImports.add("java.net.URLEncoder"); connectivityImports.add("java.io.UnsupportedEncodingException"); entityClassFile.addImports(connectivityImports); } public void addGatewayFields(Map<String[], String> keys, Map<String[], String> fields, JavaSourceFileEditor entityClassFile) throws Exception { // Add Keys for (Map.Entry<String[], String> key : keys.entrySet()) { addKeyInGWJavaFile(keys, entityClassFile, key); } // Add Fields if (!fields.isEmpty()) { for (Map.Entry<String[], String> field : fields.entrySet()) { addRemoteFieldInGWJavaFile(entityClassFile, field); addRemoteFieldInPersistenceMethods(entityClassFile, field); } } } public void addKeyInGWJavaFile(Map<String[], String> keys, JavaSourceFileEditor entityClassFile, Map.Entry<String[], String> key) { //Map ODataFieldTypes to JavaTypes String oDataType = key.getValue(); String javaType = GwUtils.odataToJavaType(oDataType); String localFieldName = key.getKey()[1]; JavaSourceFieldBuilder fieldBuilder = new JavaSourceFieldBuilder().fieldPrefix("private") .fieldType(javaType).fieldName(localFieldName).fieldValue(""); if (localFieldName.equals("Id")) fieldBuilder = fieldBuilder.fieldAnnotations("@Id\n\t@Column(name = \"id\")"); // \t@GeneratedValue(strategy = GenerationType.AUTO)\n if (key.getValue().equals("DateTime")) fieldBuilder = fieldBuilder .fieldAnnotations("@Temporal(TemporalType.TIMESTAMP)\n\t@DateTimeFormat(style=\"M-\")"); JavaSourceField fieldDeclaration = fieldBuilder.build(); entityClassFile.addGlobalField(fieldDeclaration); if (!localFieldName.equals("Id")) { JavaSourceMethod getMethod = new JavaSourceMethodBuilder() .methodName("get" + StringUtils.capitalize(localFieldName)).methodPrefix("public") .returnType(javaType).methodBody("\t\t" + "return" + " " + "this." + localFieldName + ";\n") .build(); entityClassFile.addMethod(getMethod); ArrayList<String> parameters = new ArrayList<String>(); parameters.add(javaType + " " + localFieldName); JavaSourceMethod setMethod = new JavaSourceMethodBuilder() .methodName("set" + StringUtils.capitalize(localFieldName)).methodPrefix("public") .returnType("void").parameters(parameters) .methodBody("\t\t" + "this." + localFieldName + " = " + localFieldName + ";\n").build(); entityClassFile.addMethod(setMethod); } } public void addRemoteFieldInGWJavaFile(JavaSourceFileEditor entityClassFile, Map.Entry<String[], String> field) throws Exception { String fieldName = field.getKey()[1]; //Map ODataFieldTypes to JavaTypes String oDataType = field.getValue(); String javaType = GwUtils.odataToJavaType(oDataType); addFieldInGWJavaFile(entityClassFile, fieldName, javaType); } public void addFieldInGWJavaFile(JavaSourceFileEditor entityClassFile, String fieldName, String javaType) throws Exception { addFieldInGWJavaFile(entityClassFile, fieldName, "", javaType, ""); } private void addFieldInGWJavaFile(JavaSourceFileEditor entityClassFile, String fieldName, String fieldValue, String javaType, String annotations) throws Exception { JavaSourceFieldBuilder fieldBuilder = new JavaSourceFieldBuilder().fieldPrefix("private") .fieldType(javaType).fieldName(fieldName).fieldValue(fieldValue); if (javaType.toLowerCase().contains("date") || javaType.toLowerCase().contains("calendar")) annotations += "\n\t" + "@Temporal(TemporalType.TIMESTAMP)\n\t@DateTimeFormat(style=\"M-\")"; fieldBuilder = fieldBuilder.fieldAnnotations(annotations); JavaSourceField fieldDeclaration = fieldBuilder.build(); if (entityClassFile.fieldExists(fieldDeclaration.getFieldName())) throw new Exception("Field \"" + fieldDeclaration.getFieldName() + "\" already exists in java class file " + entityClassFile.CLASS_NAME); entityClassFile.addGlobalField(fieldDeclaration); JavaSourceMethod getMethod = new JavaSourceMethodBuilder() .methodName("get" + StringUtils.capitalize(fieldName)).methodPrefix("public").returnType(javaType) .methodBody("\t\t" + "return" + " " + "this." + fieldName + ";\n").build(); entityClassFile.addMethod(getMethod); ArrayList<String> parameters = new ArrayList<String>(); parameters.add(javaType + " " + fieldName); JavaSourceMethod setMethod = new JavaSourceMethodBuilder() .methodName("set" + StringUtils.capitalize(fieldName)).methodPrefix("public").returnType("void") .parameters(parameters).methodBody("\t\t" + "this." + fieldName + " = " + fieldName + ";\n") .build(); entityClassFile.addMethod(setMethod); } private String makeGWPersistFieldCode(String tabLevels, Map.Entry<String[], String> fieldObj) { String reversedCast = "", dateTimeOffsetCastStart = "", dateTimeOffsetCastEnd = ""; String remoteFieldName = fieldObj.getKey()[0]; String localFieldName = fieldObj.getKey()[1]; if (fieldObj.getValue().equals("DateTimeOffset")) { reversedCast = "datetimeOffset"; dateTimeOffsetCastStart = "new DateTime("; dateTimeOffsetCastEnd = ")"; } else { reversedCast = GwUtils.generateReversedCast(GwUtils.odataToJavaType(fieldObj.getValue())); } return tabLevels + ".properties(OProperties." + reversedCast + "(\"" + remoteFieldName + "\", " + dateTimeOffsetCastStart + "get" + StringUtils.capitalize(localFieldName) + "()" + dateTimeOffsetCastEnd + "))\n"; } public Map.Entry<String[], String> getValidatedField(String localClassName, String fieldName, JavaSourceFileEditor entityClassFile) throws Exception { Map.Entry<String[], String> fieldObj = null; if (!entityClassFile.fieldExists(fieldName)) { String nameSpace = GwUtils.getNamespaceFromClass(entityClassFile); Map<String[], String> fields = getFieldsOfRemoteEntity(localClassName, nameSpace); for (Map.Entry<String[], String> field : fields.entrySet()) { if (field.getKey()[0].equals(fieldName)) { fieldObj = field; break; } } } else { throw new Exception("Field \"" + fieldName + "\" already exists in java class file " + localClassName); } return fieldObj; } private boolean existsDateFieldInController(String remoteEntity) { SortedSet<FileDetails> files = fileManager.findMatchingAntPath( getSubPackagePath(web) + SEPARATOR + remoteEntity + "Controller_Roo_Controller*.aj"); for (FileDetails file : files) { InputStream inputStream = fileManager.getInputStream(file.getCanonicalPath()); try { if (IOUtils.toString(inputStream) .contains(remoteEntity + "Controller.addDateTimeFormatPatterns(")) { IOUtils.closeQuietly(inputStream); return true; } } catch (IOException e) { e.printStackTrace(); } IOUtils.closeQuietly(inputStream); } return false; } public void addRelationships(Map<String, String[]> relationships, String remoteEntityName, JavaSourceFileEditor entityClassFile) throws Exception { //TODO: still to add many to many relationships in persistence methods !!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (relationships.isEmpty()) return; entityClassFile.addImport("javax.persistence.CascadeType"); entityClassFile.addImport("org.odata4j.core.OLink"); entityClassFile.addImport("org.odata4j.core.OLinks"); for (Map.Entry<String, String[]> relation : relationships.entrySet()) { int currentEntityIndex = relation.getValue()[0].equals(remoteEntityName) ? 0 : 2; String fieldName = relation.getKey(); String fieldValue = ""; String javaType = relation.getValue()[2 - currentEntityIndex]; if (getJavaFileEditor(domain, javaType, false) != null) { /* * We process the types of association (many to many, many to one, one to one, one to many) */ String associationType = ""; String multiplicityEnd1 = relation.getValue()[currentEntityIndex].contains("1") ? "One" : "Many"; String multiplicityEnd2 = relation.getValue()[3 - currentEntityIndex].contains("1") ? "One" : "Many"; associationType = multiplicityEnd1 + "To" + multiplicityEnd2; addRelationshipInPersistenceMethods(entityClassFile, fieldName, javaType, associationType); if (multiplicityEnd2.equals("Many")) { entityClassFile.addImport("java.util.HashSet"); entityClassFile.addImport("java.util.Set"); javaType = "Set<" + javaType + ">"; fieldValue = "new Hash" + javaType + "()"; } entityClassFile.addImport("javax.persistence." + associationType); addFieldInGWJavaFile(entityClassFile, fieldName, fieldValue, javaType, "@" + associationType); } } } private void addRelationshipInPersistenceMethods(JavaSourceFileEditor entityClassFile, String nav, String javaType, String associationType) { // TODO Auto-generated method stub ArrayList<JavaSourceMethod> globalMethodList = entityClassFile.getGlobalMethodList(); String pluralRemoteEntity = GwUtils.getInflectorPlural(entityClassFile.CLASS_NAME, Locale.ENGLISH); String smallRemoteEntity = StringUtils.uncapitalize(entityClassFile.CLASS_NAME); for (JavaSourceMethod method : globalMethodList) { String methodName = method.getMethodName(); /* * We insert the relation in the persist and merge methods */ if (methodName.endsWith("persist")) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); methodBody.insert(methodBody.lastIndexOf("newEntity = newEntityRequest"), makeGWPersistRelationshipCode(nav, javaType, associationType, "\t\t")); method.setMethodBody(methodBody.toString()); } else if (methodName.endsWith("merge")) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); methodBody.insert(methodBody.lastIndexOf("boolean modifyRequest = modifyEntityRequest"), makeGWMergeRelationshipCode(nav, javaType, associationType, "\t\t")); method.setMethodBody(methodBody.toString()); } /* * We insert the relation in the findAll and find<Entity>Entries methods */ else if (methodName.endsWith("findAll" + pluralRemoteEntity) || methodName.endsWith("find" + entityClassFile.CLASS_NAME + "Entries")) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); int insertPosition = methodBody.indexOf("} catch (Exception relationshipsException)"); boolean isFirstManyToMany = true; if ("OneToMany ManyToMany".contains(associationType)) { String manyToManyInsertReferenceString = StringUtils.uncapitalize(entityClassFile.CLASS_NAME) + "Link.isCollection()) {"; if (methodBody.indexOf(manyToManyInsertReferenceString) > -1) { insertPosition = methodBody.indexOf(manyToManyInsertReferenceString); isFirstManyToMany = false; } } methodBody.insert(insertPosition, makeGWShowRelationshipCode(entityClassFile.CLASS_NAME, smallRemoteEntity + "Instance", smallRemoteEntity + "Item", ODATA_KEY, nav, javaType, associationType, "\t\t", isFirstManyToMany)); method.setMethodBody(methodBody.toString()); } /* * We insert the relation in the find<Entity> method */ else if (methodName.endsWith("find" + entityClassFile.CLASS_NAME)) { StringBuffer methodBody = new StringBuffer(method.getMethodBody()); int insertPosition = methodBody.indexOf("} catch (Exception relationshipsException)"); boolean isFirstManyToMany = true; if ("OneToMany ManyToMany".contains(associationType)) { String manyToManyInsertReferenceString = StringUtils.uncapitalize(entityClassFile.CLASS_NAME) + "Link.isCollection()) {"; if (methodBody.indexOf(manyToManyInsertReferenceString) > -1) { insertPosition = methodBody.indexOf(manyToManyInsertReferenceString); isFirstManyToMany = false; } } methodBody.insert(insertPosition, makeGWShowRelationshipCode(entityClassFile.CLASS_NAME, "virtual" + entityClassFile.CLASS_NAME, smallRemoteEntity, "OEntityKey.parse(" + GwUtils.GW_CONNECTION_FIELD_NAME + ".getDecodedRemoteKey(Id))", nav, javaType, associationType, "\t\t\t", isFirstManyToMany)); method.setMethodBody(methodBody.toString()); } } } private String makeGWMergeRelationshipCode(String nav, String javaType, String associationType, String tabs) { String returnString = ""; if ("ManyToOne OneToOne".contains(associationType)) { returnString += "OEntity linked" + javaType + " = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.getEntity(\"" + javaType + "\", " + javaType + ".getRemote" + javaType + "(" + nav + ".getId())).execute();\n"; returnString += tabs + "modifyEntityRequest = modifyEntityRequest.link(\"" + nav + "\", linked" + javaType + ");\n" + tabs; } return returnString; } private String makeGWPersistRelationshipCode(String nav, String javaType, String associationType, String tabs) { String returnString = ""; if ("ManyToOne OneToOne".contains(associationType)) { returnString += "OEntity linked" + javaType + " = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.getEntity(\"" + javaType + "\", " + javaType + ".getRemote" + javaType + "(" + nav + ".getId())).execute();\n"; returnString += tabs + "newEntityRequest = newEntityRequest.link(\"" + nav + "\", linked" + javaType + ");\n" + tabs; } return returnString; } private String makeGWShowRelationshipCode(String className, String virtualLocalEntityInstance, String remoteEntity, String remoteId, String nav, String javaType, String associationType, String tabs, boolean isFirstManyToMany) { String smallClassName = StringUtils.uncapitalize(className); String capitalNav = StringUtils.capitalize(nav); String returnString = ""; if ("ManyToOne OneToOne".contains(associationType)) { returnString += "OEntity remote" + javaType + " = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.getEntity(\"" + className + "\", " + remoteId + ").nav(\"" + nav + "\").execute();\n"; returnString += tabs + javaType + " virtual" + javaType + " = " + javaType + ".find" + javaType + "(remote" + javaType + ".getEntityKey().toKeyString());\n"; returnString += tabs + virtualLocalEntityInstance + ".set" + javaType + "(virtual" + javaType + ");\n" + tabs; } else { String extraReturn = ""; if (isFirstManyToMany) { returnString += "List<OLink> " + smallClassName + "Links = " + remoteEntity + ".getLinks();\n" + tabs + "\tfor(OLink " + smallClassName + "Link : " + smallClassName + "Links) {\n" + tabs + "\t\tif (" + smallClassName + "Link.isCollection()) {\n"; extraReturn = "\n"; } returnString += extraReturn + tabs + "\t\t\tif (" + smallClassName + "Link.getTitle().equals(\"" + nav + "\")) {\n" + tabs + "\t\t\t\tSet<" + javaType + "> virtual" + javaType + "List = new HashSet<" + javaType + ">();\n" + tabs + "\t\t\t\tList<OEntity> remote" + javaType + "List = " + GwUtils.GW_CONNECTION_FIELD_NAME + ".rooODataConsumer.getEntities(" + "OLinks.relatedEntities(" + smallClassName + "Link.getRelation(), " + smallClassName + "Link.getTitle(), " + smallClassName + "Link.getHref()))\n" + tabs + "\t\t\t\t.execute().toList();\n" + tabs + "\t\t\t\tfor (OEntity remote" + javaType + " : remote" + javaType + "List) {\n" + tabs + "\t\t\t\t\t" + javaType + " virtual" + javaType + " = " + javaType + ".find" + javaType + "(remote" + javaType + ".getEntityKey().toKeyString());\n" + tabs + "\t\t\t\t\tvirtual" + javaType + "List.add(virtual" + javaType + ");\n" + tabs + "\t\t\t\t}\n" + tabs + "\t\t\t\t" + virtualLocalEntityInstance + ".set" + capitalNav + "(virtual" + javaType + "List);\n" + tabs + "\t\t\t}\n"; if (isFirstManyToMany) returnString += tabs + "\t\t}\n" + tabs + "}\n" + tabs; } return returnString; } }