Java tutorial
/** * 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.ssatguru.camel.salesforce.maven; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Field; import java.net.URI; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.apache.camel.component.salesforce.SalesforceEndpointConfig; import org.apache.camel.component.salesforce.SalesforceHttpClient; import org.apache.camel.component.salesforce.SalesforceLoginConfig; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase; import org.apache.camel.component.salesforce.api.dto.GlobalObjects; import org.apache.camel.component.salesforce.api.dto.PickListValue; import org.apache.camel.component.salesforce.api.dto.SObject; import org.apache.camel.component.salesforce.api.dto.SObjectDescription; import org.apache.camel.component.salesforce.api.dto.SObjectField; import org.apache.camel.component.salesforce.internal.PayloadFormat; import org.apache.camel.component.salesforce.internal.SalesforceSession; import org.apache.camel.component.salesforce.internal.client.DefaultRestClient; import org.apache.camel.component.salesforce.internal.client.RestClient; import org.apache.camel.component.salesforce.internal.client.SyncResponseCallback; import org.apache.camel.util.IntrospectionSupport; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.jsse.SSLContextParameters; import org.apache.log4j.Logger; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.log.Log4JLogChute; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; //camel 2.18.0 uses fasterxml rather than codehaus //import org.codehaus.jackson.map.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper; import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.ProxyConfiguration; import org.eclipse.jetty.client.Socks4Proxy; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.util.BasicAuthentication; import org.eclipse.jetty.client.util.DigestAuthentication; import org.eclipse.jetty.util.ssl.SslContextFactory; /** * Goal to generate DTOs for Salesforce SObjects */ @Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES) public class CamelSalesforceMojo extends AbstractMojo { // default connect and call timeout protected static final int DEFAULT_TIMEOUT = 60000; private static final String JAVA_EXT = ".java"; private static final String PACKAGE_NAME_PATTERN = "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)+\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"; private static final String SOBJECT_POJO_VM = "/sobject-pojo.vm"; private static final String SOBJECT_QUERY_RECORDS_VM = "/sobject-query-records.vm"; private static final String SOBJECT_PICKLIST_VM = "/sobject-picklist.vm"; private static final String SOBJECT_LOOKUP_VM = "/sobject-lookup.vm"; // used for velocity logging, to avoid creating velocity.log private static final Logger LOG = Logger.getLogger(CamelSalesforceMojo.class.getName()); /** * HTTP client properties. */ @Parameter protected Map<String, Object> httpClientProperties; /** * SSL Context parameters. */ @Parameter(property = "camelSalesforce.sslContextParameters") protected SSLContextParameters sslContextParameters; /** * HTTP Proxy host. */ @Parameter(property = "camelSalesforce.httpProxyHost") protected String httpProxyHost; /** * HTTP Proxy port. */ @Parameter(property = "camelSalesforce.httpProxyPort") protected Integer httpProxyPort; /** * Is it a SOCKS4 Proxy? */ @Parameter(property = "camelSalesforce.isHttpProxySocks4") protected boolean isHttpProxySocks4; /** * Is HTTP Proxy secure, i.e. using secure sockets, true by default. */ @Parameter(property = "camelSalesforce.isHttpProxySecure") protected boolean isHttpProxySecure = true; /** * Addresses to Proxy. */ @Parameter(property = "camelSalesforce.httpProxyIncludedAddresses") protected Set<String> httpProxyIncludedAddresses; /** * Addresses to NOT Proxy. */ @Parameter(property = "camelSalesforce.httpProxyExcludedAddresses") protected Set<String> httpProxyExcludedAddresses; /** * Proxy authentication username. */ @Parameter(property = "camelSalesforce.httpProxyUsername") protected String httpProxyUsername; /** * Proxy authentication password. */ @Parameter(property = "camelSalesforce.httpProxyPassword") protected String httpProxyPassword; /** * Proxy authentication URI. */ @Parameter(property = "camelSalesforce.httpProxyAuthUri") protected String httpProxyAuthUri; /** * Proxy authentication realm. */ @Parameter(property = "camelSalesforce.httpProxyRealm") protected String httpProxyRealm; /** * Proxy uses Digest authentication. */ @Parameter(property = "camelSalesforce.httpProxyUseDigestAuth") protected boolean httpProxyUseDigestAuth; /** * Salesforce client id. */ @Parameter(property = "camelSalesforce.clientId", required = true) protected String clientId; /** * Salesforce client secret. */ @Parameter(property = "camelSalesforce.clientSecret", required = true) protected String clientSecret; /** * Salesforce username. */ @Parameter(property = "camelSalesforce.userName", required = true) protected String userName; /** * Salesforce password. */ @Parameter(property = "camelSalesforce.password", required = true) protected String password; /** * Salesforce API version. */ @Parameter(property = "camelSalesforce.version", defaultValue = SalesforceEndpointConfig.DEFAULT_VERSION) protected String version; /** * Location of generated DTO files, defaults to * target/generated-sources/camel-salesforce. */ @Parameter(property = "camelSalesforce.outputDirectory", defaultValue = "${project.build.directory}/generated-sources/camel-salesforce") protected File outputDirectory; /** * Salesforce login URL, defaults to https://login.salesforce.com. */ @Parameter(property = "camelSalesforce.loginUrl", defaultValue = SalesforceLoginConfig.DEFAULT_LOGIN_URL) protected String loginUrl; /** * Names of Salesforce SObject for which DTOs must be generated. */ @Parameter protected String[] includes; /** * Do NOT generate DTOs for these Salesforce SObjects. */ @Parameter protected String[] excludes; /** * Include Salesforce SObjects that match pattern. */ @Parameter(property = "camelSalesforce.includePattern") protected String includePattern; /** * Exclude Salesforce SObjects that match pattern. */ @Parameter(property = "camelSalesforce.excludePattern") protected String excludePattern; /** * Java package name for generated DTOs. */ @Parameter(property = "camelSalesforce.packageName", defaultValue = "org.apache.camel.salesforce.dto") protected String packageName; @Parameter(property = "camelSalesforce.useStringsForPicklists", defaultValue = "false") protected Boolean useStringsForPicklists; /** * sObject to External id mappings */ @Parameter protected Map sObjectExternalIdMap; private VelocityEngine engine; private long responseTimeout; /** * Execute the mojo to generate SObject DTOs * * @throws MojoExecutionException */ public void execute() throws MojoExecutionException { // initialize velocity to load resources from class loader and use Log4J Properties velocityProperties = new Properties(); velocityProperties.setProperty(RuntimeConstants.RESOURCE_LOADER, "cloader"); velocityProperties.setProperty("cloader.resource.loader.class", ClasspathResourceLoader.class.getName()); velocityProperties.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, Log4JLogChute.class.getName()); velocityProperties.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM + ".log4j.logger", LOG.getName()); engine = new VelocityEngine(velocityProperties); engine.init(); // make sure we can load both templates if (!engine.resourceExists(SOBJECT_POJO_VM) || !engine.resourceExists(SOBJECT_QUERY_RECORDS_VM)) { throw new MojoExecutionException("Velocity templates not found"); } // connect to Salesforce final SalesforceHttpClient httpClient = createHttpClient(); final SalesforceSession session = httpClient.getSession(); getLog().info("Salesforce login..."); try { session.login(null); } catch (SalesforceException e) { String msg = "Salesforce login error " + e.getMessage(); throw new MojoExecutionException(msg, e); } getLog().info("Salesforce login successful"); // create rest client RestClient restClient; try { restClient = new DefaultRestClient(httpClient, version, PayloadFormat.JSON, session); // remember to start the active client object ((DefaultRestClient) restClient).start(); } catch (Exception e) { final String msg = "Unexpected exception creating Rest client: " + e.getMessage(); throw new MojoExecutionException(msg, e); } try { // use Jackson json final ObjectMapper mapper = new ObjectMapper(); // call getGlobalObjects to get all SObjects final Set<String> objectNames = new TreeSet<String>(); final SyncResponseCallback callback = new SyncResponseCallback(); try { getLog().info("Getting Salesforce Objects..."); restClient.getGlobalObjects(callback); if (!callback.await(responseTimeout, TimeUnit.MILLISECONDS)) { throw new MojoExecutionException("Timeout waiting for getGlobalObjects!"); } final SalesforceException ex = callback.getException(); if (ex != null) { throw ex; } final GlobalObjects globalObjects = mapper.readValue(callback.getResponse(), GlobalObjects.class); // create a list of object names for (SObject sObject : globalObjects.getSobjects()) { objectNames.add(sObject.getName()); } } catch (Exception e) { String msg = "Error getting global Objects " + e.getMessage(); throw new MojoExecutionException(msg, e); } // check if we are generating POJOs for all objects or not if ((includes != null && includes.length > 0) || (excludes != null && excludes.length > 0) || ObjectHelper.isNotEmpty(includePattern) || ObjectHelper.isNotEmpty(excludePattern)) { filterObjectNames(objectNames); } else { getLog().warn(String.format("Generating Java classes for all %s Objects, this may take a while...", objectNames.size())); } // for every accepted name, get SObject description final Set<SObjectDescription> descriptions = new HashSet<SObjectDescription>(); getLog().info("Retrieving Object descriptions..."); for (String name : objectNames) { try { callback.reset(); restClient.getDescription(name, callback); if (!callback.await(responseTimeout, TimeUnit.MILLISECONDS)) { throw new MojoExecutionException("Timeout waiting for getDescription for sObject " + name); } final SalesforceException ex = callback.getException(); if (ex != null) { throw ex; } descriptions.add(mapper.readValue(callback.getResponse(), SObjectDescription.class)); } catch (Exception e) { String msg = "Error getting SObject description for '" + name + "': " + e.getMessage(); throw new MojoExecutionException(msg, e); } } // create package directory // validate package name if (!packageName.matches(PACKAGE_NAME_PATTERN)) { throw new MojoExecutionException("Invalid package name " + packageName); } final File pkgDir = new File(outputDirectory, packageName.trim().replace('.', File.separatorChar)); if (!pkgDir.exists()) { if (!pkgDir.mkdirs()) { throw new MojoExecutionException("Unable to create " + pkgDir); } } getLog().info("Generating Java Classes..."); // generate POJOs for every object description final GeneratorUtility utility = new GeneratorUtility(useStringsForPicklists, sObjectExternalIdMap); // should we provide a flag to control timestamp generation? final String generatedDate = new Date().toString(); for (SObjectDescription description : descriptions) { processDescription(pkgDir, description, utility, generatedDate); } getLog().info(String.format("Successfully generated %s Java Classes", descriptions.size() * 2)); } finally { // remember to stop the client try { ((DefaultRestClient) restClient).stop(); } catch (Exception ignore) { } // Salesforce session stop try { session.stop(); } catch (Exception ignore) { } // release HttpConnections try { httpClient.stop(); } catch (Exception ignore) { } } } protected void filterObjectNames(Set<String> objectNames) throws MojoExecutionException { getLog().info("Looking for matching Object names..."); // create a list of accepted names final Set<String> includedNames = new HashSet<String>(); if (includes != null && includes.length > 0) { for (String name : includes) { name = name.trim(); if (name.isEmpty()) { throw new MojoExecutionException("Invalid empty name in includes"); } includedNames.add(name); } } final Set<String> excludedNames = new HashSet<String>(); if (excludes != null && excludes.length > 0) { for (String name : excludes) { name = name.trim(); if (name.isEmpty()) { throw new MojoExecutionException("Invalid empty name in excludes"); } excludedNames.add(name); } } // check whether a pattern is in effect Pattern incPattern; if (includePattern != null && !includePattern.trim().isEmpty()) { incPattern = Pattern.compile(includePattern.trim()); } else if (includedNames.isEmpty()) { // include everything by default if no include names are set incPattern = Pattern.compile(".*"); } else { // include nothing by default if include names are set incPattern = Pattern.compile("^$"); } // check whether a pattern is in effect Pattern excPattern; if (excludePattern != null && !excludePattern.trim().isEmpty()) { excPattern = Pattern.compile(excludePattern.trim()); } else { // exclude nothing by default excPattern = Pattern.compile("^$"); } final Set<String> acceptedNames = new HashSet<String>(); for (String name : objectNames) { // name is included, or matches include pattern // and is not excluded and does not match exclude pattern if ((includedNames.contains(name) || incPattern.matcher(name).matches()) && !excludedNames.contains(name) && !excPattern.matcher(name).matches()) { acceptedNames.add(name); } } objectNames.clear(); objectNames.addAll(acceptedNames); getLog().info(String.format("Found %s matching Objects", objectNames.size())); } protected SalesforceHttpClient createHttpClient() throws MojoExecutionException { final SalesforceHttpClient httpClient; // set ssl context parameters try { final SSLContextParameters contextParameters = sslContextParameters != null ? sslContextParameters : new SSLContextParameters(); final SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setSslContext(contextParameters.createSSLContext()); httpClient = new SalesforceHttpClient(sslContextFactory); } catch (GeneralSecurityException e) { throw new MojoExecutionException("Error creating default SSL context: " + e.getMessage(), e); } catch (IOException e) { throw new MojoExecutionException("Error creating default SSL context: " + e.getMessage(), e); } // default settings httpClient.setConnectTimeout(DEFAULT_TIMEOUT); httpClient.setTimeout(DEFAULT_TIMEOUT); // enable redirects, no need for a RedirectListener class in Jetty 9 httpClient.setFollowRedirects(true); // set HTTP client parameters if (httpClientProperties != null && !httpClientProperties.isEmpty()) { try { IntrospectionSupport.setProperties(httpClient, new HashMap<String, Object>(httpClientProperties)); } catch (Exception e) { throw new MojoExecutionException("Error setting HTTP client properties: " + e.getMessage(), e); } } // wait for 1 second longer than the HTTP client response timeout responseTimeout = httpClient.getTimeout() + 1000L; // set http proxy settings // set HTTP proxy settings if (this.httpProxyHost != null && httpProxyPort != null) { Origin.Address proxyAddress = new Origin.Address(this.httpProxyHost, this.httpProxyPort); ProxyConfiguration.Proxy proxy; if (isHttpProxySocks4) { proxy = new Socks4Proxy(proxyAddress, isHttpProxySecure); } else { proxy = new HttpProxy(proxyAddress, isHttpProxySecure); } if (httpProxyIncludedAddresses != null && !httpProxyIncludedAddresses.isEmpty()) { proxy.getIncludedAddresses().addAll(httpProxyIncludedAddresses); } if (httpProxyExcludedAddresses != null && !httpProxyExcludedAddresses.isEmpty()) { proxy.getExcludedAddresses().addAll(httpProxyExcludedAddresses); } httpClient.getProxyConfiguration().getProxies().add(proxy); } if (this.httpProxyUsername != null && httpProxyPassword != null) { ObjectHelper.notEmpty(httpProxyAuthUri, "httpProxyAuthUri"); ObjectHelper.notEmpty(httpProxyRealm, "httpProxyRealm"); final Authentication authentication; if (httpProxyUseDigestAuth) { authentication = new DigestAuthentication(URI.create(httpProxyAuthUri), httpProxyRealm, httpProxyUsername, httpProxyPassword); } else { authentication = new BasicAuthentication(URI.create(httpProxyAuthUri), httpProxyRealm, httpProxyUsername, httpProxyPassword); } httpClient.getAuthenticationStore().addAuthentication(authentication); } // set session before calling start() final SalesforceSession session = new SalesforceSession(httpClient, httpClient.getTimeout(), new SalesforceLoginConfig(loginUrl, clientId, clientSecret, userName, password, false)); httpClient.setSession(session); try { httpClient.start(); } catch (Exception e) { throw new MojoExecutionException("Error creating HTTP client: " + e.getMessage(), e); } return httpClient; } private void processDescription(File pkgDir, SObjectDescription description, GeneratorUtility utility, String generatedDate) throws MojoExecutionException { // generate a source file for SObject String fileName = description.getName() + JAVA_EXT; BufferedWriter writer = null; try { final File pojoFile = new File(pkgDir, fileName); writer = new BufferedWriter(new FileWriter(pojoFile)); VelocityContext context = new VelocityContext(); context.put("packageName", packageName); context.put("utility", utility); context.put("desc", description); context.put("generatedDate", generatedDate); context.put("useStringsForPicklists", useStringsForPicklists); Template pojoTemplate = engine.getTemplate(SOBJECT_POJO_VM); pojoTemplate.merge(context, writer); // close pojoFile writer.close(); // write required Enumerations for any picklists and Classes for any Lookups for (SObjectField field : description.getFields()) { if (utility.isPicklist(field) || utility.isMultiSelectPicklist(field)) { String enumName = description.getName() + "_" + utility.enumTypeName(field.getName()); fileName = enumName + JAVA_EXT; File enumFile = new File(pkgDir, fileName); writer = new BufferedWriter(new FileWriter(enumFile)); context = new VelocityContext(); context.put("packageName", packageName); context.put("utility", utility); context.put("field", field); context.put("enumName", enumName); context.put("generatedDate", generatedDate); Template queryTemplate = engine.getTemplate(SOBJECT_PICKLIST_VM); queryTemplate.merge(context, writer); // close Enum file writer.close(); } else if (utility.isLookup(field)) { String sObjectName = field.getReferenceTo().get(0); String externalIdName = (String) sObjectExternalIdMap.get(sObjectName); String className = sObjectName + "Lookup"; fileName = className + JAVA_EXT; File lkupFile = new File(pkgDir, fileName); writer = new BufferedWriter(new FileWriter(lkupFile)); context = new VelocityContext(); context.put("packageName", packageName); context.put("utility", utility); context.put("className", className); context.put("sObjectName", sObjectName); context.put("externalIdName", externalIdName); context.put("generatedDate", generatedDate); Template queryTemplate = engine.getTemplate(SOBJECT_LOOKUP_VM); queryTemplate.merge(context, writer); // close lookup class file writer.close(); } } // write the QueryRecords class fileName = "QueryRecords" + description.getName() + JAVA_EXT; File queryFile = new File(pkgDir, fileName); writer = new BufferedWriter(new FileWriter(queryFile)); context = new VelocityContext(); context.put("packageName", packageName); context.put("desc", description); context.put("generatedDate", generatedDate); Template queryTemplate = engine.getTemplate(SOBJECT_QUERY_RECORDS_VM); queryTemplate.merge(context, writer); // close QueryRecords file writer.close(); } catch (Exception e) { String msg = "Error creating " + fileName + ": " + e.getMessage(); throw new MojoExecutionException(msg, e); } finally { if (writer != null) { try { writer.close(); } catch (IOException ignore) { } } } } public static class GeneratorUtility { private static final Set<String> BASE_FIELDS; private static final Map<String, String> LOOKUP_MAP; static { BASE_FIELDS = new HashSet<String>(); for (Field field : AbstractSObjectBase.class.getDeclaredFields()) { BASE_FIELDS.add(field.getName()); } // create a type map // using JAXB mapping, for the most part // uses Joda time instead of XmlGregorianCalendar // uses java.time.ZonedDateTime instead of Joda // TODO do we need support for commented types??? final String[][] typeMap = new String[][] { { "ID", "String" }, // mapping for tns:ID SOAP type { "string", "String" }, { "integer", "java.math.BigInteger" }, { "int", "Integer" }, { "long", "Long" }, { "short", "Short" }, { "decimal", "java.math.BigDecimal" }, { "float", "Float" }, { "double", "Double" }, { "boolean", "Boolean" }, { "byte", "Byte" }, // {"QName", "javax.xml.namespace.QName"}, // {"dateTime", "javax.xml.datatype.XMLGregorianCalendar"}, //{ "dateTime", "org.joda.time.DateTime" }, { "dateTime", "java.time.ZonedDateTime" }, // the blob base64Binary type is mapped to String URL for retrieving the blob { "base64Binary", "String" }, // {"hexBinary", "byte[]"}, { "unsignedInt", "Long" }, { "unsignedShort", "Integer" }, { "unsignedByte", "Short" }, // {"time", "javax.xml.datatype.XMLGregorianCalendar"}, //{ "time", "org.joda.time.DateTime" }, { "time", "java.time.ZonedDateTime" }, // {"date", "javax.xml.datatype.XMLGregorianCalendar"}, //{ "date", "org.joda.time.DateTime" }, { "date", "java.time.ZonedDateTime" }, // {"g", "javax.xml.datatype.XMLGregorianCalendar"}, //{ "g", "org.joda.time.DateTime" }, { "g", "java.time.ZonedDateTime" }, // Salesforce maps any types like string, picklist, reference, etc. to string { "anyType", "String" }, /* {"anySimpleType", "java.lang.Object"}, {"anySimpleType", "java.lang.String"}, {"duration", "javax.xml.datatype.Duration"}, {"NOTATION", "javax.xml.namespace.QName"} */ { "address", "org.apache.camel.component.salesforce.api.dto.Address" }, { "location", "org.apache.camel.component.salesforce.api.dto.GeoLocation" } }; LOOKUP_MAP = new HashMap<String, String>(); for (String[] entry : typeMap) { LOOKUP_MAP.put(entry[0], entry[1]); } } private static final String BASE64BINARY = "base64Binary"; private static final String MULTIPICKLIST = "multipicklist"; private static final String PICKLIST = "picklist"; private static final String LOOKUP = "reference"; private static final String LOOKUP_EXCLUDE = "MasterRecordId CreatedById LastModifiedById OwnerId"; private boolean useStringsForPicklists; private Map sObjectExternalIdMap; public GeneratorUtility(Boolean useStringsForPicklists, Map sObjectExternalIdMap) { this.useStringsForPicklists = Boolean.TRUE.equals(useStringsForPicklists); if (sObjectExternalIdMap == null) { this.sObjectExternalIdMap = new HashMap(); } else { this.sObjectExternalIdMap = sObjectExternalIdMap; } } public boolean isBlobField(SObjectField field) { final String soapType = field.getSoapType(); return BASE64BINARY.equals(soapType.substring(soapType.indexOf(':') + 1)); } public boolean notBaseField(String name) { return !BASE_FIELDS.contains(name); } public String getFieldType(SObjectDescription description, SObjectField field) throws MojoExecutionException { // check if this is a picklist if (isPicklist(field)) { if (useStringsForPicklists) { return String.class.getName(); } else { // use a pick list enum, which will be created after // generating the SObject class return description.getName() + "_" + enumTypeName(field.getName()); } } else if (isMultiSelectPicklist(field)) { if (useStringsForPicklists) { return String.class.getName() + "[]"; } else { // use a pick list enum array, enum will be created after // generating the SObject class return description.getName() + "_" + enumTypeName(field.getName()) + "[]"; } } else if (isLookup(field)) { return field.getReferenceTo().get(0) + "Lookup"; } else { // map field to Java type final String soapType = field.getSoapType(); final String type = LOOKUP_MAP.get(soapType.substring(soapType.indexOf(':') + 1)); if (type == null) { throw new MojoExecutionException( String.format("Unsupported type %s for field %s", soapType, field.getName())); } return type; } } public String getLookUpFieldName(SObjectField field) { String name = field.getName(); if (name.endsWith("Id")) { name = name.substring(0, name.length() - 2); } else if (name.endsWith("__c")) { // Custom Object or Custom field name = name.substring(0, name.length() - 3) + "__r"; } else if (name.endsWith("__pc")) { // Custom Person Account Field name = name.substring(0, name.length() - 4) + "__pr"; } return name; } public boolean isMultiSelectPicklist(SObjectField field) { return MULTIPICKLIST.equals(field.getType()); } public boolean hasPicklists(SObjectDescription desc) { for (SObjectField field : desc.getFields()) { if (isPicklist(field)) { return true; } } return false; } public boolean hasMultiSelectPicklists(SObjectDescription desc) { for (SObjectField field : desc.getFields()) { if (isMultiSelectPicklist(field)) { return true; } } return false; } public boolean hasLookups(SObjectDescription desc) { for (SObjectField field : desc.getFields()) { if (isLookup(field)) { return true; } } return false; } public List<PickListValue> getUniqueValues(SObjectField field) { if (field.getPicklistValues().isEmpty()) { return field.getPicklistValues(); } final List<PickListValue> result = new ArrayList<PickListValue>(); final Set<String> literals = new HashSet<String>(); for (PickListValue listValue : field.getPicklistValues()) { final String value = listValue.getValue(); if (!literals.contains(value)) { literals.add(value); result.add(listValue); } } literals.clear(); Collections.sort(result, new Comparator<PickListValue>() { @Override public int compare(PickListValue o1, PickListValue o2) { return o1.getValue().compareTo(o2.getValue()); } }); return result; } public boolean isPicklist(SObjectField field) { // return field.getPicklistValues() != null && !field.getPicklistValues().isEmpty(); return PICKLIST.equals(field.getType()); } public boolean isLookup(SObjectField field) { if (LOOKUP.equals(field.getType()) && (LOOKUP_EXCLUDE.indexOf(field.getName()) == -1)) { String referenceObject = field.getReferenceTo().get(0); String referenceTargetField = (String) sObjectExternalIdMap.get(referenceObject); if (referenceTargetField == null) { return false; } else { return true; } } else { return false; } } public String enumTypeName(String name) { name = name.endsWith("__c") ? name.substring(0, name.length() - 3) : name; return name + "Enum"; } public String getEnumConstant(String value) { // TODO add support for supplementary characters final StringBuilder result = new StringBuilder(); boolean changed = false; if (!Character.isJavaIdentifierStart(value.charAt(0))) { result.append("_"); changed = true; } for (char c : value.toCharArray()) { if (Character.isJavaIdentifierPart(c)) { result.append(c); } else { // replace non Java identifier character with '_' result.append('_'); changed = true; } } return changed ? result.toString().toUpperCase() : value.toUpperCase(); } } }