View Javadoc
1   package org.synchronoss.cpo.core.meta;
2   
3   /*-
4    * [[
5    * core
6    * ==
7    * Copyright (C) 2003 - 2026 Exaxis LLC, Synchronoss Technologies Inc
8    * ==
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ]]
23   */
24  
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  import org.synchronoss.cpo.core.CpoException;
33  import org.synchronoss.cpo.core.enums.Crud;
34  import org.synchronoss.cpo.core.meta.domain.*;
35  import org.synchronoss.cpo.cpometa.*;
36  
37  /**
38   * Base {@link CpoMetaAdapter} implementation that maintains the in-memory map of loaded {@link
39   * CpoClass} metadata and the shared logic for loading that metadata from the JAXB-bound {@code Ct*}
40   * classes produced by unmarshalling a CPO meta XML document. Datastore-specific subclasses supply
41   * the {@link DataTypeMapper} and the concrete {@link CpoClass}/{@link CpoAttribute}/{@link
42   * CpoFunctionGroup}/{@link CpoFunction}/{@link CpoArgument} factory methods.
43   *
44   * @author dberry
45   */
46  public abstract class AbstractCpoMetaAdapter implements CpoMetaAdapter {
47  
48    private static final Logger logger = LoggerFactory.getLogger(AbstractCpoMetaAdapter.class);
49  
50    /** The map of classes in this metaAdapter */
51    private Map<String, CpoClass> classMap = new HashMap<>();
52  
53    private CpoClass currentClass = null;
54  
55    protected AbstractCpoMetaAdapter() {
56      super();
57    }
58  
59    /**
60     * {@inheritDoc}
61     *
62     * <p>Looks up the metadata by {@code bean}'s exact class name first, then walks up the
63     * superclass/interface hierarchy until a match is found.
64     */
65    @Override
66    public <T> CpoClass getMetaClass(T bean) throws CpoException {
67      CpoClass cpoClass = null;
68      String className;
69      String requestedName;
70      List<Class<?>> classList = new ArrayList<>();
71      Class<?> requestedClass;
72  
73      if (bean != null) {
74        requestedClass = bean.getClass();
75        classList.add(requestedClass);
76        requestedName = requestedClass.getName();
77        className = requestedName;
78        cpoClass = classMap.get(className);
79  
80        while (cpoClass == null && !classList.isEmpty()) {
81          classList = getSuperClasses(classList);
82          for (Class<?> clazz : classList) {
83            className = clazz.getName();
84            cpoClass = classMap.get(className);
85            if (cpoClass != null) break;
86          }
87        }
88        if (cpoClass == null) {
89          throw new CpoException("No Metadata found for class:" + requestedName);
90        }
91      }
92  
93      return cpoClass;
94    }
95  
96    private List<Class<?>> getSuperClasses(List<Class<?>> classList) {
97      List<Class<?>> superClassList = new ArrayList<>();
98  
99      for (Class<?> clazz : classList) {
100       Class<?> superClass = clazz.getSuperclass();
101       if (superClass != null) {
102         superClassList.add(superClass);
103       }
104       superClassList.addAll(Arrays.asList(clazz.getInterfaces()));
105     }
106     return superClassList;
107   }
108 
109   /** {@inheritDoc} */
110   @Override
111   public List<CpoClass> getCpoClasses() {
112     List<CpoClass> result = new ArrayList<>();
113     result.addAll(classMap.values());
114     return result;
115   }
116 
117   /** {@inheritDoc} */
118   @Override
119   public String getDataTypeName(CpoAttribute attribute) {
120     String clazzName = getDataTypeJavaClass(attribute).getName();
121     byte[] b = new byte[0];
122     char[] c = new char[0];
123 
124     if (b.getClass().getName().equals(clazzName)) {
125       clazzName = "byte[]";
126     } else if (c.getClass().getName().equals(clazzName)) {
127       clazzName = "char[]";
128     }
129     return clazzName;
130   }
131 
132   /** {@inheritDoc} */
133   @Override
134   public Class<?> getDataTypeJavaClass(CpoAttribute attribute) {
135     Class<?> clazz = String.class;
136     DataTypeMapEntry<?> dataTypeMapEntry =
137         getDataTypeMapper().getDataTypeMapEntry(attribute.getDataType());
138 
139     if (attribute.getTransformInMethod() != null) {
140       clazz = attribute.getTransformInMethod().getReturnType();
141     } else if (attribute.getGetterReturnType() != null) {
142       clazz = attribute.getGetterReturnType();
143     } else if (dataTypeMapEntry != null) {
144       clazz = dataTypeMapEntry.javaClass();
145     }
146 
147     return clazz;
148   }
149 
150   /** {@inheritDoc} */
151   @Override
152   public int getDataTypeInt(String dataTypeName) throws CpoException {
153     return getDataTypeMapper().getDataTypeInt(dataTypeName);
154   }
155 
156   /** {@inheritDoc} */
157   @Override
158   public DataTypeMapEntry<?> getDataTypeMapEntry(int dataTypeInt) throws CpoException {
159     return getDataTypeMapper().getDataTypeMapEntry(dataTypeInt);
160   }
161 
162   /** {@inheritDoc} */
163   @Override
164   public List<String> getAllowableDataTypes() throws CpoException {
165     return getDataTypeMapper().getDataTypeNames();
166   }
167 
168   /**
169    * Gets the datastore-specific data type mapper used to resolve native data types to Java classes.
170    *
171    * @return the data type mapper for this meta adapter
172    */
173   protected abstract DataTypeMapper getDataTypeMapper();
174 
175   protected void loadCpoMetaDataDocument(CtCpoMetaData ctCpoMetaData, boolean caseSensitive)
176       throws CpoException {
177     for (CtClass ctClass : ctCpoMetaData.getCpoClass()) {
178 
179       CpoClass cpoClass = getCpoClass(ctClass.getName());
180       if (cpoClass == null) {
181         cpoClass = createCpoClass(caseSensitive);
182         cpoClass.setName(ctClass.getName());
183         cpoClass.setDescription(ctClass.getDescription());
184         loadCpoClass(cpoClass, ctClass);
185         addCpoClass(cpoClass);
186       } else {
187         loadCpoClass(cpoClass, ctClass);
188       }
189     }
190   }
191 
192   protected void loadCpoClass(CpoClass cpoClass, CtClass ctClass) throws CpoException {
193     logger.debug("Loading class: " + ctClass.getName());
194 
195     currentClass = cpoClass;
196 
197     logger.debug("Loading " + ctClass.getCpoAttribute().size() + " attributes ");
198 
199     for (var jaxbElement : ctClass.getCpoAttribute()) {
200       CtAttribute ctAttribute = jaxbElement.getValue();
201       logger.debug("ctAttribute: " + ctAttribute);
202       logger.debug("ctAttribute.getValue(): " + ctAttribute);
203       logger.debug("ctAttribute.getValue().getJavaName(): " + ctAttribute.getJavaName());
204       CpoAttribute cpoAttribute = cpoClass.getAttributeJava(ctAttribute.getJavaName());
205       logger.debug("cpoAttribute: " + cpoAttribute);
206 
207       if (cpoAttribute == null) {
208         cpoAttribute = createCpoAttribute();
209         logger.debug("loading attribute: " + ctAttribute.getJavaName());
210         loadCpoAttribute(cpoAttribute, ctAttribute);
211         logger.debug("loaded attribute: " + ctAttribute.getJavaName());
212         cpoClass.addAttribute(cpoAttribute);
213       } else {
214         loadCpoAttribute(cpoAttribute, ctAttribute);
215       }
216     }
217 
218     for (CtFunctionGroup ctFunctionGroup : ctClass.getCpoFunctionGroup()) {
219       CpoFunctionGroup functionGroup = null;
220 
221       try {
222         functionGroup =
223             cpoClass.getFunctionGroup(
224                 Crud.valueOf(ctFunctionGroup.getType().toString()), ctFunctionGroup.getName());
225       } catch (Exception e) {
226         // this a runtime exception that we can ignore during load time.
227         if (logger.isTraceEnabled()) {
228           logger.trace(e.getLocalizedMessage());
229         }
230       }
231 
232       if (functionGroup == null) {
233         functionGroup = createCpoFunctionGroup();
234         loadCpoFunctionGroup(functionGroup, ctFunctionGroup);
235         cpoClass.addFunctionGroup(functionGroup);
236       } else {
237         functionGroup.clearFunctions();
238         loadCpoFunctionGroup(functionGroup, ctFunctionGroup);
239       }
240       logger.debug("Added Function Group: " + functionGroup.getName());
241     }
242   }
243 
244   protected void loadCpoAttribute(CpoAttribute cpoAttribute, CtAttribute ctAttribute) {
245     cpoAttribute.setDataName(ctAttribute.getDataName());
246     cpoAttribute.setDataType(ctAttribute.getDataType());
247     cpoAttribute.setDescription(ctAttribute.getDescription());
248     cpoAttribute.setJavaName(ctAttribute.getJavaName());
249     cpoAttribute.setJavaType(ctAttribute.getJavaType());
250     cpoAttribute.setTransformClassName(ctAttribute.getTransformClass());
251   }
252 
253   protected void loadCpoFunctionGroup(
254       CpoFunctionGroup cpoFunctionGroup, CtFunctionGroup ctFunctionGroup) {
255     cpoFunctionGroup.setDescription(ctFunctionGroup.getDescription());
256     cpoFunctionGroup.setName(ctFunctionGroup.getName());
257     cpoFunctionGroup.setType(ctFunctionGroup.getType().toString());
258 
259     for (CtFunction ctFunction : ctFunctionGroup.getCpoFunction()) {
260       CpoFunction cpoFunction = createCpoFunction();
261       cpoFunctionGroup.addFunction(cpoFunction);
262       loadCpoFunction(cpoFunction, ctFunction);
263     }
264   }
265 
266   protected void loadCpoFunction(CpoFunction cpoFunction, CtFunction ctFunction) {
267     cpoFunction.setName(ctFunction.getName());
268     cpoFunction.setExpression(ctFunction.getExpression());
269     cpoFunction.setDescription(ctFunction.getDescription());
270 
271     for (var jaxbElement : ctFunction.getCpoArgument()) {
272       CtArgument ctArgument = jaxbElement.getValue();
273       CpoArgument cpoArgument = createCpoArgument();
274       cpoFunction.addArgument(cpoArgument);
275       logger.debug("loading argument: " + ctArgument.getAttributeName());
276       loadCpoArgument(cpoArgument, ctArgument);
277       logger.debug("loaded argument: " + ctArgument.getAttributeName());
278     }
279   }
280 
281   protected void loadCpoArgument(CpoArgument cpoArgument, CtArgument ctArgument) {
282     cpoArgument.setName(ctArgument.getAttributeName());
283     cpoArgument.setDescription(ctArgument.getDescription());
284 
285     cpoArgument.setAttribute(currentClass.getAttributeJava(ctArgument.getAttributeName()));
286   }
287 
288   protected CpoClass createCpoClass(boolean caseSensitive) {
289     if (caseSensitive) {
290       return new CpoClassCaseSensitive();
291     } else {
292       return new CpoClassCaseInsensitive();
293     }
294   }
295 
296   protected CpoAttribute createCpoAttribute() {
297     return new CpoAttribute();
298   }
299 
300   protected CpoFunctionGroup createCpoFunctionGroup() {
301     return new CpoFunctionGroup();
302   }
303 
304   protected CpoFunction createCpoFunction() {
305     return new CpoFunction();
306   }
307 
308   protected CpoArgument createCpoArgument() {
309     return new CpoArgument();
310   }
311 
312   protected CpoClass getCpoClass(String name) {
313     return classMap.get(name);
314   }
315 
316   protected void addCpoClass(CpoClass metaClass) {
317     CpoClass oldMetaClass = classMap.put(metaClass.getName(), metaClass);
318     if (oldMetaClass != null) {
319       logger.debug("Overwrote class: " + metaClass.getName());
320     } else {
321       logger.debug("Added class: " + metaClass.getName());
322     }
323   }
324 
325   protected void removeCpoClass(CpoClass metaClass) {
326     if (metaClass != null) {
327       logger.debug("Removing class: " + metaClass.getName());
328       metaClass.emptyMaps();
329       classMap.remove(metaClass.getName());
330     }
331   }
332 
333   protected void removeAllCpoClass() {
334     classMap.clear();
335   }
336 }