1 package com.sourceforge.jpatterns.utils;
2
3 import com.sourceforge.jpatterns.core.JPConstants;
4 import com.sourceforge.jpatterns.core.JPException;
5 import com.sourceforge.jpatterns.core.configuration.exceptions.JPConfigException;
6 import com.sourceforge.jpatterns.core.configuration.exceptions.JPInitializationException;
7 import com.sourceforge.jpatterns.core.configuration.model.JPatternsConfigBaseBean;
8 import com.sourceforge.jpatterns.schema.CastorGroupTypeItem;
9 import com.sourceforge.jpatterns.schema.CastorNameScopePriorityType;
10 import com.sourceforge.jpatterns.schema.CastorSectionType;
11 import com.sourceforge.jpatterns.schema.Config;
12 import com.sourceforge.jpatterns.schema.Factory;
13 import com.sourceforge.jpatterns.schema.JPatternsConfig;
14 import com.sourceforge.jpatterns.schema.JPatternsConfigItem;
15 import com.zmicer.utils.InputArgumentUtils;
16 import com.zmicer.utils.LoggingUtils;
17 import com.zmicer.utils.ReflexionUtils;
18 import com.zmicer.utils.StringUtils;
19 import org.apache.log4j.Logger;
20 import org.exolab.castor.xml.Unmarshaller;
21
22 import java.io.File;
23 import java.io.FileReader;
24 import java.lang.reflect.Method;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28
29 /**
30 * This class contains the castor utils related to the JPatterns project (appropriate project specific model is used here).
31 * <p/>
32 * $Author:: zmicer $<br/>
33 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
34 */
35 public class CastorUtils
36 {
37 /**
38 * Logger instance.
39 */
40 final public static Logger LOG = Logger.getLogger(CastorUtils.class);
41
42 /**
43 * Base name of the class to be used.
44 */
45 final protected static String GROUP_ITEM_CLASS_NAME = CastorGroupTypeItem.class.getName();
46
47 /**
48 * Get <code>JPatternsConfig</code> using the provided file.
49 *
50 * @param file File using which we need to obtain <code>JPatternsConfig</code>
51 * Can not be null (otherwise <code>IllegalArgumentException</code> would appear), also should point to the existed file
52 * (otherwise <code>IllegalArgumentException</code> would appear).
53 *
54 * @return the castor object, <code>JPatternsConfig</code>. In the case of the failure it returns <code>null</null>
55 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
56 */
57 public static JPatternsConfig getJPatternsConfig(final File file)
58 {
59 InputArgumentUtils.checkObjects(file);
60 if (!file.isFile())
61 {
62 throw new IllegalArgumentException("The provided object is not file.");
63 }
64 try
65 {
66 FileReader fileReader = new FileReader(file);
67 Unmarshaller unmarshaller = new Unmarshaller(JPatternsConfig.class);
68 Object object = unmarshaller.unmarshal(fileReader);
69 if (null == object)
70 {
71 final String message = "Can not instantiate the object using unmarshal operation.";
72 LOG.debug(message);
73 throw new IllegalStateException(message);
74 }
75 if (!(object instanceof JPatternsConfig))
76 {
77 final String message = "The object instantiated using operation unmarshal should be the type JPatternsConfig.";
78 LOG.debug(message);
79 throw new IllegalStateException(message);
80 }
81 return (JPatternsConfig) object;
82 }
83 catch (Exception e)
84 {
85 LoggingUtils.logException(LOG, e, null, null);
86 throw new JPInitializationException(e);
87 }
88 }
89
90 /**
91 * Stores and retrive all the objects of CastorSectionType (it means all the objects of this type and also child of this type).
92 * This would be performed trough the reflexion. This method is necessary because it doesn could be used without any changes in
93 * the case some new types - childs of CastorSectionType would be added - so we won't need to change the sources.
94 *
95 * @param config JPatternsConfig object to be parsed. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
96 *
97 * @return List of <code>CastorSectionType</code> objects, could be empty List returned in the case the provided config doesn't store
98 * the necessary objects
99 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
100 */
101 public static List<CastorSectionType> extractCastorSectionTypeObjects(final JPatternsConfig config)
102 {
103 InputArgumentUtils.checkObjects(config);
104
105 final List<CastorSectionType> result = new ArrayList<CastorSectionType>();
106 final JPatternsConfigItem[] configItems = config.getJPatternsConfigItem();
107
108 // no information
109 if (configItems.length == 0)
110 {
111 return result;
112 }
113 final JPatternsConfigItem configItem = new JPatternsConfigItem();
114 // 00: obtain the methods and find the methods the names of which start with "get" and return object of CastorSectionType
115 final String methodPrefix = "get";
116 final Method[] methods = configItem.getClass().getMethods();
117 final List<Method> methodsWeNeed = new ArrayList<Method>();
118 for (final Method method : methods)
119 {
120 final Class returnedType = method.getReturnType();
121 // && returnedType.isArray() - earlier this method checked if array is returned
122 if (method.getName().contains(methodPrefix) && !method.getName().contains("getClass"))
123 {
124 //final String arrayMembersClassName = ReflexionUtils.getClassNameOfArrayMembers(returnedType);
125 // commented still could be used later
126 try
127 {
128 final Object objectToCheck = returnedType.newInstance();
129 if (objectToCheck instanceof CastorSectionType)
130 {
131 methodsWeNeed.add(method);
132 }
133 }
134 catch (Exception e)
135 {
136 // just log and continue with finding the methods we need.
137 LoggingUtils.logException(LOG, e, null, null);
138 }
139 }
140 }
141
142 // 01: iterate through the methods we have obtained, call them on the provided castor object, using the reflexion and accumulate
143 // the results to the result storage. Check the results of the method are the thing we need.
144 for (final JPatternsConfigItem item : configItems)
145 {
146 for (final Method method : methodsWeNeed)
147 {
148 try
149 {
150 final Object object = method.invoke(item);
151 // if (object instanceof CastorSectionType[]) was earlier
152 if (object instanceof CastorSectionType)
153 {
154 result.add((CastorSectionType) object);
155 }
156 }
157 catch (Exception ex)
158 {
159 LoggingUtils.logException(LOG, ex, null, null);
160 }
161 }
162 }
163 return result;
164 }
165
166 /**
167 * Extract all the possible CastorNameScopePriorityType castor objects from the provided CastorSectionType. The sense of this method is
168 * as follows: CastorSectionType is a base "sections" object and has lots of child with the business items which are the instances of
169 * the class CastorNameScopePriorityType. In the case of CastorFactoryType, CastorConfigType it would be just method getItem returning
170 * Item[] which are instances of CastorNameScopePriorityType too. But for the castorGroupType it is the method getCastorGroupTypeItem
171 * returning CastorGroupTypeItem[] each of them is a choice - in this case we would call "getChoiceValue" method.
172 *
173 * @param sectionType the section type to be analyzed. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
174 *
175 * @return the List of the <code>CastorNameScopePriorityType</code> objects.
176 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
177 */
178 public static List<CastorNameScopePriorityType> extractCastorNameScopePriorityTypeObjects(final CastorSectionType sectionType)
179 {
180 InputArgumentUtils.checkObjects(sectionType);
181 final String methodPrefix = "get";
182 final String choiceValueMethod = "getChoiceValue";
183 final String groupItemName = "";
184
185 final Method[] methods = sectionType.getClass().getMethods();
186 final List<Method> methodsWeNeed = new ArrayList<Method>();
187 for (final Method method : methods)
188 {
189 final Class returnedType = method.getReturnType();
190 if (method.getName().contains(methodPrefix) && returnedType.isArray())
191 {
192 final String arrayMembersClassName = ReflexionUtils.getClassNameOfArrayMembers(returnedType);
193 try
194 {
195 // todo [zmicer]: not sure if we need the reflexion here - may be string contains method would be enough?
196 final Object objectToCheck = sectionType.getClass().getClassLoader().loadClass(arrayMembersClassName).newInstance();
197 if (objectToCheck instanceof CastorNameScopePriorityType || objectToCheck instanceof CastorGroupTypeItem)
198 {
199 methodsWeNeed.add(method);
200 }
201 }
202 catch (Exception e)
203 {
204 // just log and continue with finding the methods we need.
205 LoggingUtils.logException(LOG, e, null, null);
206 }
207 }
208 }
209
210 // 01: iterate through the methods we have obtained, call them on the provided castor object, using the reflexion and accumulate
211 // the results to the result storage. Check the results of the method are the thing we need.
212 final List<CastorNameScopePriorityType> result = new ArrayList<CastorNameScopePriorityType>();
213 for (final Method method : methodsWeNeed)
214 {
215 try
216 {
217 final Object object = method.invoke(sectionType);
218 // if (object instanceof CastorSectionType[]) was earlier
219 if (null != object)
220 {
221 if (object instanceof CastorNameScopePriorityType[])
222 {
223 result.addAll(Arrays.asList((CastorNameScopePriorityType[]) object));
224 }
225 else if (object instanceof CastorGroupTypeItem[])
226 {
227 CastorGroupTypeItem[] groupItems = (CastorGroupTypeItem[]) object;
228 for (CastorGroupTypeItem groupItem : groupItems)
229 {
230 if (groupItem.getChoiceValue() instanceof CastorNameScopePriorityType)
231 {
232 result.add((CastorNameScopePriorityType) groupItem.getChoiceValue());
233 }
234 }
235 }
236 }
237 }
238 catch (Exception ex)
239 {
240 LoggingUtils.logException(LOG, ex, null, null);
241 }
242 }
243 return result;
244 }
245
246 /**
247 * Make the scopes of all the elements here normalized. Currently (possibly smth. could be changed) it means:
248 * <br/>
249 * 1. the scopes of the root/child elements can be different
250 * 2. in the case "business" item is defined with scope and parent without - the defined scope would have power on the business item
251 * 3. in the case the parent elements is defined with a scope, then all the childs of it would be of the appropriate scope
252 * <br/>
253 * The default scope is mentioned is either the default scope of the castor root config or
254 * <code>com.sourceforge.jpatterns.core.JPConstants.DEFAULT_SCOPE_NAME</code>
255 * <br/>
256 * note1 [zmicer]: think over the adjustment of principles of normalization, as some new types could appear here, and this method would
257 * be extended (new scope supported types different from the base type CastorSectionType would appear).
258 * note2 [zmicer]: earlier at this method we retrict the case when parent and child have different scopes - for now it is allowed (it
259 * was decided to allow the support of scopes of the business item elements inside the section - it is one more scope visibility area).
260 *
261 * @param config castor root config objects to be processed.
262 * Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
263 *
264 * @version 1.1
265 */
266 public static void validateAndNormalizeScopesPriorities(final JPatternsConfig config)
267 {
268 InputArgumentUtils.checkObjects(config);
269
270 final boolean configHasDefaultScope = (null != config.getDefaultScope() && !"".equals(config.getDefaultScope()));
271 // 00: default scope
272 final String defaultScope = (configHasDefaultScope) ? config.getDefaultScope() :
273 com.sourceforge.jpatterns.core.JPConstants.DEFAULT_SCOPE_NAME;
274
275 if (!configHasDefaultScope)
276 {
277 config.setDefaultScope(defaultScope);
278 }
279
280 // 01: working with sections
281 final List<CastorSectionType> castorSectionTypes = CastorUtils.extractCastorSectionTypeObjects(config);
282 for (final CastorSectionType castorSectionType : castorSectionTypes)
283 {
284 if (null == castorSectionType.getScope() || "".equals(castorSectionType.getScope()))
285 {
286 castorSectionType.setScope(defaultScope);
287 }
288 setValidatePriority(castorSectionType, false);
289
290 // 02: working with items
291 final List<CastorNameScopePriorityType> items = extractCastorNameScopePriorityTypeObjects(castorSectionType);
292 for (final CastorNameScopePriorityType item : items)
293 {
294 // 02:a - set the scope of the parent
295 if (null == item.getScope() || "".equals(item.getScope()))
296 {
297 item.setScope(castorSectionType.getScope());
298 }
299 setValidatePriority(item, false);
300 }
301 }
302 }
303
304 /**
305 * All the items of the provided <code>JPatternsConfig</code> - sections, business items - are made prioritized. For this
306 * <code>com.sourceforge.jpatterns.core.JPConstants.PRIORITIZED_PRIOTITY_PREFIX</code> would be added as the prefix to all the
307 * priorities. Then later, during the prioritized merging this items would have priority over the identical, but without this prefix.
308 * <br/>
309 * This functionality is introduced to make the algorithm of simlple merging of JPatterns configuration files identical with the
310 * merging with priority (when some configuration takes the priority).
311 *
312 * @param castorConfig the target JPatternsConfig root castor object objects of which should be modified (their priorities)
313 * Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
314 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
315 */
316 public static void makePrioritized(final JPatternsConfig castorConfig)
317 {
318 InputArgumentUtils.checkObjects(castorConfig);
319 // 01: working with sections
320 final List<CastorSectionType> castorSectionTypes = CastorUtils.extractCastorSectionTypeObjects(castorConfig);
321 for (final CastorSectionType castorSectionType : castorSectionTypes)
322 {
323 setValidatePriority(castorSectionType, true);
324
325 // 02: working with items
326 final List<CastorNameScopePriorityType> items = extractCastorNameScopePriorityTypeObjects(castorSectionType);
327 for (final CastorNameScopePriorityType item : items)
328 {
329 setValidatePriority(item, true);
330 }
331 }
332 }
333
334 /**
335 * Make prioritized the provided bean.
336 *
337 * @param bean the instance to be prioritized.
338 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
339 */
340 public static void makePrioritized(final JPatternsConfigBaseBean bean)
341 {
342 InputArgumentUtils.checkObjects(bean);
343 // 01: working with sections
344 final List<CastorSectionType> castorSectionTypes = bean.getListOfSectionItems();
345 for (final CastorSectionType castorSectionType : castorSectionTypes)
346 {
347 setValidatePriority(castorSectionType, true);
348
349 // 02: working with items
350 final List<CastorNameScopePriorityType> items = extractCastorNameScopePriorityTypeObjects(castorSectionType);
351 for (final CastorNameScopePriorityType item : items)
352 {
353 setValidatePriority(item, true);
354 }
355 }
356 }
357
358 /**
359 * Set new / validate (is any) the priority field of the given castor object.
360 *
361 * @param castor CastorNameScopePriorityType instance. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
362 * @param makePrioritized signs if we need to make the priority prioritized (using JPConstants.PRIORITIZED_PRIOTITY_PREFIX)
363 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
364 */
365 protected static void setValidatePriority(final CastorNameScopePriorityType castor, final boolean makePrioritized)
366 {
367 InputArgumentUtils.checkObjects(castor);
368 if (null == castor.getPriority() || "".equals(castor.getPriority()))
369 {
370 castor.setPriority(String.valueOf(JPConstants.DEFAULT_PRIORITY));
371 }
372 // check if it is already prioritized.
373 if (!castor.getPriority().contains(JPConstants.PRIORITIZED_PRIOTITY_PREFIX))
374 {
375 if (null == StringUtils.getInteger(castor.getPriority()))
376 {
377 throw new JPConfigException("The following priority " + castor.getPriority() + " is incorrect.");
378 }
379 if (makePrioritized)
380 {
381 castor.setPriority(JPConstants.PRIORITIZED_PRIOTITY_PREFIX + castor.getPriority());
382 }
383 }
384 }
385
386 /**
387 * Get the int representation of the priority.
388 *
389 * @param castor castor object storing the priority to be used.
390 * Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
391 *
392 * @return the int representaotion
393 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
394 */
395 public static int getPriority(final CastorNameScopePriorityType castor)
396 {
397 InputArgumentUtils.checkObjects(castor);
398 InputArgumentUtils.checkStrings(true, castor.getPriority());
399 String workingStr = null;
400 if (castor.getPriority().startsWith(JPConstants.PRIORITIZED_PRIOTITY_PREFIX))
401 {
402 workingStr = castor.getPriority().substring(JPConstants.PRIORITIZED_PRIOTITY_PREFIX.length());
403 }
404 else
405 {
406 workingStr = castor.getPriority();
407 }
408 final Integer integer = StringUtils.getInteger(workingStr);
409 if (null == integer)
410 {
411 throw new IllegalArgumentException("Invalid priority is set to the castor [" + castor.getPriority() + "]");
412 }
413 return integer;
414 }
415
416 /**
417 * Get the List of <code>Config</code> castor objects by the root JPatterns configuration object. It is necessary for the convinient
418 * opering with the Config object.
419 *
420 * @param config the <code>JPatternsConfig</code> root castor config objects,
421 * Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
422 *
423 * @return the List of the Config castor object we need, the empty List is returned in the case there are not Config objects.
424 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
425 */
426 public static List<Config> getConfig(final JPatternsConfig config)
427 {
428 InputArgumentUtils.checkObjects(config);
429 final List<Config> result = new ArrayList<Config>();
430 for (JPatternsConfigItem configItem : config.getJPatternsConfigItem())
431 {
432 if (null != configItem.getConfig())
433 {
434 result.add(configItem.getConfig());
435 }
436 }
437 return result;
438 }
439
440 /**
441 * Get the List of <code>Factory</code> castor objects by the root JPatterns configuration object. It is necessary for the convinient
442 * opering with the Factory object.
443 *
444 * @param config the <code>JPatternsConfig</code> root castor config objects,
445 * Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
446 *
447 * @return the List of the Factory castor object we need, the empty List is returned in the case there are not Config objects.
448 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
449 */
450 public static List<Factory> getFactory(final JPatternsConfig config)
451 {
452 InputArgumentUtils.checkObjects(config);
453 final List<Factory> result = new ArrayList<Factory>();
454 for (JPatternsConfigItem configItem : config.getJPatternsConfigItem())
455 {
456 if (null != configItem.getFactory())
457 {
458 result.add(configItem.getFactory());
459 }
460 }
461 return result;
462 }
463
464 /**
465 * Add the instance of <code>Config</code> type to the provided JPatternsConfig root config object
466 *
467 * @param config JPatternsConfig, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
468 * @param configItem Config castor object. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
469 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
470 */
471 public static void addConfig(final JPatternsConfig config, final Config configItem)
472 {
473 InputArgumentUtils.checkObjects(config, configItem);
474 JPatternsConfigItem item = new JPatternsConfigItem();
475 item.setConfig(configItem);
476 config.addJPatternsConfigItem(item);
477 }
478
479 /**
480 * Add the instance of <code>Config</code> type to the provided JPatternsConfig root config object
481 *
482 * @param config JPatternsConfig, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
483 * @param factoryItem Factory castor object. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
484 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
485 */
486 public static void addFactory(final JPatternsConfig config, final Factory factoryItem)
487 {
488 InputArgumentUtils.checkObjects(config, factoryItem);
489 JPatternsConfigItem item = new JPatternsConfigItem();
490 item.setFactory(factoryItem);
491 config.addJPatternsConfigItem(item);
492 }
493
494 /**
495 * Construct the CastorGroupTypeItem (choice object) using the provided Object.
496 *
497 * @param object CastorNameScopePriorityType instance, can not be null, CastorGroupTypeItem should have appropriate method
498 * set***, where *** - the base name of the provided object. Otherwise IllegalArgumentException would appeared
499 *
500 * @return the constructed CastorGroupTypeItem
501 * $Rev:: 67 $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
502 */
503 public static CastorGroupTypeItem constructGroupItem(final CastorNameScopePriorityType object)
504 {
505 InputArgumentUtils.checkObjects(object);
506 final String typeToBeAdded = ReflexionUtils.getBaseName(object.getClass());
507 final Method[] methods = CastorGroupTypeItem.class.getMethods();
508 for (final Method method : methods)
509 {
510 if (method.getName().equals("set" + typeToBeAdded))
511 {
512 try
513 {
514 CastorGroupTypeItem groupItem = new CastorGroupTypeItem();
515 method.invoke(groupItem, object);
516 return groupItem;
517 }
518 catch (Exception ex)
519 {
520 LoggingUtils.logException(LOG, ex, "Can not instantiate CastorGroupTypeItem", null);
521 throw new JPException("Can not instantiate CastorGroupTypeItem", ex);
522 }
523 }
524 }
525 return null;
526 }
527
528 /**
529 * Get the String representation of the Item in the format we need (the full path would be provided).
530 * note [zmicer]: be noticed the both name and scope values should be filled for these object passed here
531 *
532 * @param section the Section object for which we need to get the full representation in the format we need
533 * Could be null, in this case information about the section is displayed
534 * @param item Item castor object. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
535 * Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
536 *
537 * @return the String representation we could use later.
538 */
539 public static String toString(final CastorSectionType section, final CastorNameScopePriorityType item)
540 {
541 InputArgumentUtils.checkObjects(item);
542 InputArgumentUtils.checkStrings(true, item.getScope(), item.getName());
543 String result = "";
544 if (null != section)
545 {
546 InputArgumentUtils.checkStrings(true, section.getScope(), section.getName());
547 result += "Section: scope [" + section.getScope() + "], name [" + section.getName() + "];";
548 }
549 return result += "Item: scope [" + item.getScope() + "], name [" + item.getName() + "];";
550 }
551 }