/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.core.salesforce.adapter; import java.net.MalformedURLException; import java.net.URL; import org.codehaus.jettison.json.JSONArray; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.orcid.core.salesforce.model.CommunityType; import org.orcid.core.salesforce.model.Contact; import org.orcid.core.salesforce.model.ContactRole; import org.orcid.core.salesforce.model.ContactRoleType; import org.orcid.core.salesforce.model.Member; import org.orcid.core.salesforce.model.Opportunity; import org.springframework.beans.factory.FactoryBean; import ma.glasnost.orika.CustomConverter; import ma.glasnost.orika.CustomMapper; import ma.glasnost.orika.MapperFacade; import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.MappingContext; import ma.glasnost.orika.MappingException; import ma.glasnost.orika.converter.ConverterFactory; import ma.glasnost.orika.impl.DefaultMapperFactory; import ma.glasnost.orika.metadata.ClassMapBuilder; import ma.glasnost.orika.metadata.Property; import ma.glasnost.orika.metadata.Type; import ma.glasnost.orika.property.IntrospectorPropertyResolver; /** * * @author Will Simpson * */ public class SalesForceMapperFacadeFactory implements FactoryBean<MapperFacade> { @Override public MapperFacade getObject() throws Exception { return getMemberMapperFacade(); } @Override public Class<MapperFacade> getObjectType() { return MapperFacade.class; } @Override public boolean isSingleton() { return true; } public MapperFacade getMemberMapperFacade() throws Exception { MapperFactory mapperFactory = new DefaultMapperFactory.Builder().propertyResolverStrategy(new JSONPropertyResolver()).build(); ConverterFactory converterFactory = mapperFactory.getConverterFactory(); converterFactory.registerConverter(new StringConverter()); converterFactory.registerConverter(new URLConverter()); converterFactory.registerConverter(new ReverseURLConverter()); registerMemberMap(mapperFactory); registerOpportunityMap(mapperFactory); registerContactMap(mapperFactory); registerContactRoleMap(mapperFactory); return mapperFactory.getMapperFacade(); } public void registerMemberMap(MapperFactory mapperFactory) { ClassMapBuilder<Member, JSONObject> classMap = mapperFactory.classMap(Member.class, JSONObject.class).mapNulls(false).mapNullsInReverse(false); ConverterFactory converterFactory = mapperFactory.getConverterFactory(); converterFactory.registerConverter(new CommunityTypeConverter()); converterFactory.registerConverter(new ReverseCommunityTypeConverter()); classMap.field("id", "Id"); classMap.field("name", "Name"); classMap.field("publicDisplayName", "Public_Display_Name__c"); classMap.field("websiteUrl", "Website"); classMap.field("researchCommunity", "Research_Community__c"); classMap.field("country", "BillingCountry"); classMap.field("description", "Public_Display_Description__c"); classMap.field("logoUrl", "Logo_Description__c"); classMap.field("publicDisplayEmail", "Public_Display_Email__c"); classMap.customize(new CustomMapper<Member, JSONObject>() { @Override public void mapBtoA(JSONObject b, Member a, MappingContext context) { JSONObject opportunitiesObject = b.optJSONObject("Opportunities"); if (opportunitiesObject != null) { JSONArray recordsArray = opportunitiesObject.optJSONArray("records"); if (recordsArray != null && recordsArray.length() > 0) { try { JSONObject first = recordsArray.getJSONObject(0); Object urlObj = first.getJSONObject("attributes").opt("url"); a.setMainOpportunityPath(JSONObject.NULL.equals(urlObj) ? null : urlObj.toString()); Object consortiumLeadIdObj = first.opt("Consortia_Lead__c"); a.setConsortiumLeadId(JSONObject.NULL.equals(consortiumLeadIdObj) ? null : consortiumLeadIdObj.toString()); } catch (JSONException e) { throw new RuntimeException("Error reading first opportunity record", e); } } } } }); classMap.register(); } private void registerOpportunityMap(MapperFactory mapperFactory) { ClassMapBuilder<Opportunity, JSONObject> classMap = mapperFactory.classMap(Opportunity.class, JSONObject.class).mapNulls(false).mapNullsInReverse(false); classMap.field("id", "Id"); classMap.field("targetAccountId", "AccountId"); classMap.field("stageName", "StageName"); classMap.field("closeDate", "CloseDate"); classMap.field("type", "Type"); classMap.field("memberType", "member_type__c"); classMap.field("membershipStartDate", "Membership_Start_Date__c"); classMap.field("membershipEndDate", "Membership_End_Date__c"); classMap.field("consortiumLeadId", "Consortia_Lead__c"); classMap.field("name", "Name"); classMap.field("recordTypeId", "RecordTypeId"); classMap.register(); } private void registerContactMap(MapperFactory mapperFactory) { ClassMapBuilder<Contact, JSONObject> classMap = mapperFactory.classMap(Contact.class, JSONObject.class).mapNulls(false).mapNullsInReverse(false); classMap.field("id", "Id"); classMap.field("orcid", "ORCID_iD_Path__c"); classMap.fieldAToB("firstName", "FirstName"); classMap.fieldAToB("lastName", "LastName"); classMap.fieldAToB("email", "Email"); classMap.fieldAToB("accountId", "AccountId"); classMap.fieldBToA("Member_Org_Role__c", "role.roleType"); classMap.fieldBToA("Contact__r.FirstName", "firstName"); classMap.fieldBToA("Contact__r.LastName", "lastName"); classMap.fieldBToA("Contact__r.Email", "email"); classMap.fieldBToA("Contact__c", "id"); classMap.fieldBToA("Contact__c", "role.contactId"); classMap.fieldBToA("AccountId", "role.accountId"); classMap.fieldBToA("Id", "role.id"); classMap.register(); } private void registerContactRoleMap(MapperFactory mapperFactory) { ConverterFactory converterFactory = mapperFactory.getConverterFactory(); converterFactory.registerConverter(new ContactRoleConverter()); converterFactory.registerConverter(new ReverseContactRoleConverter()); ClassMapBuilder<ContactRole, JSONObject> classMap = mapperFactory.classMap(ContactRole.class, JSONObject.class).mapNulls(false).mapNullsInReverse(false); classMap.field("id", "Id"); classMap.field("accountId", "Organization__c"); classMap.field("contactId", "Contact__c"); classMap.field("roleType", "Member_Org_Role__c"); classMap.register(); } private static class JSONPropertyResolver extends IntrospectorPropertyResolver { /** Add this string to the property name to handle as a JSON Array **/ private static final String ARRAY_EXPRESSION_SUFFIX = "_array"; /** Use this property name to get the first item from a JSONArray **/ private static final String FIRST_ITEM_FROM_ARRAY_EXPRESSION = "first"; protected Property getProperty(java.lang.reflect.Type type, String expr, boolean isNestedLookup, Property owner) throws MappingException { Property property = null; try { property = super.getProperty(type, expr, isNestedLookup, null); } catch (MappingException e) { try { if (expr.endsWith(ARRAY_EXPRESSION_SUFFIX)) { String key = expr.substring(0, expr.indexOf('_')); property = super.resolveInlineProperty(type, expr + ":{optJSONArray(\"" + key + "\")|put(\"" + key + "\",%s)|type=" + (isNestedLookup ? "org.codehaus.jettison.json.JSONArray" : "Object") + "}"); } else if (FIRST_ITEM_FROM_ARRAY_EXPRESSION.equals(expr)) { property = super.resolveInlineProperty(type, FIRST_ITEM_FROM_ARRAY_EXPRESSION + ":{opt(0)|put(0, %s)|type=org.codehaus.jettison.json.JSONObject}"); } else { property = super.resolveInlineProperty(type, expr + ":{opt(\"" + expr + "\")|put(\"" + expr + "\",%s)|type=" + (isNestedLookup ? "org.codehaus.jettison.json.JSONObject" : "Object") + "}"); } } catch (MappingException e2) { throw e; // throw the original exception } } return property; } } private class StringConverter extends CustomConverter<Object, String> { @Override public String convert(Object source, Type<? extends String> destinationType) { if (JSONObject.NULL.equals(source)) { return null; } return source.toString(); } } private class URLConverter extends CustomConverter<URL, Object> { @Override public Object convert(URL source, Type<? extends Object> destinationType) { return source.toString(); } } private class ReverseURLConverter extends CustomConverter<Object, URL> { @Override public URL convert(Object source, Type<? extends URL> destinationType) { String s = source.toString(); if (!s.startsWith("http")) { s += "http://"; } try { return new URL(s); } catch (MalformedURLException e) { return null; } } } private class ContactRoleConverter extends CustomConverter<ContactRoleType, Object> { @Override public Object convert(ContactRoleType source, Type<? extends Object> destinationType) { return source.value(); } } private class ReverseContactRoleConverter extends CustomConverter<Object, ContactRoleType> { @Override public ContactRoleType convert(Object source, Type<? extends ContactRoleType> destinationType) { return ContactRoleType.fromValue(source.toString()); } } private class CommunityTypeConverter extends CustomConverter<CommunityType, Object> { @Override public Object convert(CommunityType source, Type<? extends Object> destinationType) { return source.value(); } } private class ReverseCommunityTypeConverter extends CustomConverter<Object, CommunityType> { @Override public CommunityType convert(Object source, Type<? extends CommunityType> destinationType) { return CommunityType.fromValue(source.toString()); } } }