View Javadoc

1   package com.sourceforge.jpatterns.core.configuration.model;
2   
3   import com.sourceforge.jpatterns.core.JPConstants;
4   import com.sourceforge.jpatterns.core.configuration.PropertiesBasedFactory;
5   import com.sourceforge.jpatterns.core.configuration.PropertiesProvider;
6   import com.sourceforge.jpatterns.core.configuration.exceptions.JPConfigException;
7   import com.sourceforge.jpatterns.core.configuration.exceptions.JPInitializationException;
8   import com.sourceforge.jpatterns.schema.CastorConfigType;
9   import com.sourceforge.jpatterns.schema.CastorFactoryType;
10  import com.sourceforge.jpatterns.schema.CastorGroupType;
11  import com.sourceforge.jpatterns.schema.CastorGroupTypeItem;
12  import com.sourceforge.jpatterns.schema.CastorNameScopePriorityType;
13  import com.sourceforge.jpatterns.schema.CastorSectionType;
14  import com.sourceforge.jpatterns.schema.Item;
15  import com.sourceforge.jpatterns.schema.JPatternsConfig;
16  import com.sourceforge.jpatterns.utils.CastorUtils;
17  import com.zmicer.utils.InputArgumentUtils;
18  import com.zmicer.utils.ObjectStateUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Map;
25  
26  /**
27   * Default, "native" implementation of the appropriate interface.
28   *
29   * $Author:: zmicer             $<br/>
30   * $Rev:: 67                    $<br/> * $Date:: 2007-08-28 21:37:07 #$<br/>
31   * $Date:: 2007-08-28 21:37:07 #$<br/>
32   */
33  public class JPatternsConfigBeansBuilderImpl implements IJPatternsConfigBeansBuilder
34  {
35      /**
36       * Logger instance.
37       */
38      final public static Logger LOG = Logger.getLogger(JPatternsConfigBeansBuilderImpl.class);
39  
40      /**
41       * value for the com.sourceforge.jpatterns.core.configuration.PropertiesProvider.JPProperties.XML_CONFIG_OVERRIDING_DEPTH property
42       */
43      private static String OverridingDepth;
44  
45      /**
46       * value for the
47       * com.sourceforge.jpatterns.core.configuration.PropertiesProvider.JPProperties.XML_CONFIG_CUSTOM_OVERRIDES_NOT_DEPENDING_ON_PRIORITY
48       */
49      private static boolean OverrideNotDependingOnPriority;
50  
51      /**
52       * Initialization block.
53       */
54      {
55          PropertiesProvider propertiesProvider =
56              PropertiesProvider.getInstance(PropertiesBasedFactory.getInstance().getPropertiesManager(), false);
57          final String depth = propertiesProvider.getStringProperty(
58              com.sourceforge.jpatterns.core.configuration.PropertiesProvider.JPProperties.XML_CONFIG_OVERRIDING_DEPTH);
59          setOverridingDepth((null == depth) ? PropertiesProvider.OverridingDepths.OVERRIDING_LEVEL_SECTION.toString() : depth);
60          final Boolean property = propertiesProvider.getBooleanProperty(com.sourceforge.jpatterns.core.configuration.
61              PropertiesProvider.JPProperties.XML_CONFIG_CUSTOM_OVERRIDES_NOT_DEPENDING_ON_PRIORITY);
62          setOverrideNotDependingOnPriority((null != property) && property);
63      }
64  
65      /**
66       * @see IJPatternsConfigBeansBuilder#build(com.sourceforge.jpatterns.schema.JPatternsConfig)
67       * <br/>
68       * <pre>
69       * The algorithm is used here as follows:
70       * <strong>Sections filling</strong>:
71       * 1. the List of "section based" objects is formed<br/>
72       *      - in the case the section has not explicit scope - the default one from the root castor object or the
73       *      {@link com.sourceforge.jpatterns.core.JPConstants.DEFAULT_SCOPE_NAME} is set.
74       * 2. all the "section based" objects are put to the map
75       *      - in the case {@link com.sourceforge.jpatterns.core.configuration.PropertiesProvider.JPProperties.XML_CONFIG_OVERRIDING_DEPTH}
76       * property equals to
77       * <code>com.sourceforge.jpatterns.core.configuration.PropertiesProvider.OverridingDepths.OVERRIDING_LEVEL_ITEM</code> then in the case
78       * of two identical sections (scopes, section name and priority), they would be merged to the one by joining the appropriate business
79       * items. If there are identical business items (scopes, name and priority) - the
80       * <code>com.sourceforge.jpatterns.core.configuration.exceptions.JPInitializationException</code> exception would appear
81       *      - in the case {@link com.sourceforge.jpatterns.core.configuration.PropertiesProvider.JPProperties.XML_CONFIG_OVERRIDING_DEPTH}
82       * property equals to
83       * <code>com.sourceforge.jpatterns.core.configuration.PropertiesProvider.OverridingDepths.OVERRIDING_LEVEL_SECTION</code> then in the
84       * case of two identical sections (scopes, section name and priority)
85       * <code>com.sourceforge.jpatterns.core.configuration.exceptions.JPConfigException</code> exception would appear
86       *      - in the case of the identical pathes but different priorities they would be taken into consideration (in this case
87       * com.sourceforge.jpatterns.core.configuration.PropertiesProvider.JPProperties.XML_CONFIG_CUSTOM_OVERRIDES_NOT_DEPENDING_ON_PRIORITY
88       * would be considered too).
89       * <p/>
90       *      <strong>Business items filling</strong>:
91       *      todo [zmicer]: <<t.b.d.>>
92       * <p/>
93       * note [zmicer]: please be noticed the Section with the certain scope is allowed to store the business items with different scopes
94       * </pre>
95       */
96      public JPatternsConfigBean build(final JPatternsConfig config)
97      {
98          // 00: check the input params on the validness.
99          InputArgumentUtils.checkObjects(config);
100 
101         JPatternsConfigBean result = new JPatternsConfigBean();
102         // 01: set additional info
103         result.setDefaultScope(config.getDefaultScope());
104         result.setCastorConfig(config);
105 
106         // 02: fill with sections/items
107         CastorUtils.validateAndNormalizeScopesPriorities(config);
108         fill(result, CastorUtils.extractCastorSectionTypeObjects(config));
109 
110         return result;
111     }
112 
113     /**
114      * @see IJPatternsConfigBeansBuilder#build(com.sourceforge.jpatterns.schema.JPatternsConfig)
115      */
116     public JPatternsConfigsBean build(final List<JPatternsConfigBaseBean> beans)
117     {
118         InputArgumentUtils.checkObjects(beans);
119         final JPatternsConfigsBean result = new JPatternsConfigsBean();
120 
121         final List<CastorSectionType> sections = new ArrayList<CastorSectionType>();
122         for (JPatternsConfigBaseBean bean : beans)
123         {
124             ObjectStateUtils.strongCheck(bean);
125             sections.addAll(bean.getListOfSectionItems());
126         }
127         fill(result, sections);
128 
129         return result;
130     }
131 
132     /**
133      * Please review the Java Docs to the method {@link fill}
134      * todo [zmicer]: adjust Java Doc
135      * todo [zmicer]: think if this method could be moved to interface of the builder too
136      *
137      * @param initialBaseBean the initial bean to fill.
138      *                        Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
139      * @param sections        the List of the CastorSectionType objects. They should be normalized in the sense of scopes and priorities(for
140      *                        details study
141      *                        {@link com.sourceforge.jpatterns.utils.CastorUtils#validateAndNormalizeScopesPriorities(JPatternsConfig)}).
142      *                        Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
143      */
144     protected void fill(final JPatternsConfigBaseBean initialBaseBean, final List<CastorSectionType> sections)
145     {
146         InputArgumentUtils.checkObjects(initialBaseBean, sections);
147         ObjectStateUtils.strongCheck(initialBaseBean);
148 
149         // 01: working with the sections.
150         for (final CastorSectionType section : sections)
151         {
152             if (null == section.getScope() && null == section.getName())
153             {
154                 throw new JPConfigException("The scope and name of CastorSectionType can not be null.");
155             }
156             final CastorSectionType alreadySection = initialBaseBean.getSection(section.getScope(), section.getName());
157             // A. section with the same path is not existed yet or the pathes are different - just put it
158             if (null == alreadySection ||
159                 (!section.getScope().equals(alreadySection.getScope()) || !section.getName().equals(alreadySection.getName())))
160             {
161                 initialBaseBean.setSection(section.getScope(), section);
162             }
163             else
164             {
165                 initialBaseBean.setSection(section.getScope(),
166                     (CastorSectionType) choiceOrMergeCastorNameScopePriorityTypes(section, alreadySection));
167             }
168         }
169 
170         // 02: working with the business items
171         // A. iterate through all the sections we have at this moment
172         Map<String, Map<String, CastorSectionType>> sectionItems = initialBaseBean.getSectionItems();
173         for (String sectionScope : sectionItems.keySet())
174         {
175             final Map<String, CastorSectionType> sectionsMap = sectionItems.get(sectionScope);
176             for (String sectionName : sectionsMap.keySet())
177             {
178                 final CastorSectionType section = sectionsMap.get(sectionName);
179                 final List<CastorNameScopePriorityType> items = CastorUtils.extractCastorNameScopePriorityTypeObjects(section);
180                 for (CastorNameScopePriorityType item : items)
181                 {
182                     if (null == item.getScope() && null == section.getName())
183                     {
184                         throw new JPConfigException("The scope and name of Item can not be null.");
185                     }
186                     if (StringUtils.isBlank(section.getScope()))
187                     {
188                         throw new JPInitializationException("Section should have scope set.");
189                     }
190                     final CastorNameScopePriorityType alreadyItem =
191                         initialBaseBean.getBusinessItem(section.getScope(), sectionName, item.getScope(), item.getName());
192                     // A. item with the same path is not existed yet
193                     if (null == alreadyItem)
194                     {
195                         initialBaseBean.setBusinessItem(section.getScope(), sectionName, item);
196                     }
197                     else
198                     {
199                         initialBaseBean.setBusinessItem(section.getScope(), sectionName,
200                             choiceOrMergeCastorNameScopePriorityTypes(item, alreadyItem));
201                     }
202                 }
203             }
204         }
205     }
206 
207     /**
208      * Choice one of the given CastorNameScopePriorityType castor objects or perform the merging if it is necessary. Please review the
209      * comments to the main <code>fill</code> method to understand how it works (depending on the mentioned properties etc.)
210      * <br/>
211      * These objects should have the equal pathes to be passed here (scope, name) - otherwise the IllegalArgumentException appear
212      *
213      * @param castor1 the first object, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
214      * @param castor2 the second object, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
215      *
216      * @return the object is more prioritized here
217      */
218     protected CastorNameScopePriorityType choiceOrMergeCastorNameScopePriorityTypes(final CastorNameScopePriorityType castor1,
219         final CastorNameScopePriorityType castor2)
220     {
221         InputArgumentUtils.checkObjects(castor1, castor2);
222         InputArgumentUtils.checkStrings(true, castor1.getScope(), castor1.getName(), castor1.getPriority());
223         InputArgumentUtils.checkStrings(true, castor2.getScope(), castor2.getName(), castor2.getPriority());
224         if (!castor1.getScope().equals(castor2.getScope()) || !castor1.getName().equals(castor2.getName()))
225         {
226             throw new IllegalArgumentException("The provided object should have the identical pathes to be checked / compared.");
227         }
228         if (!castor1.getClass().equals(castor2.getClass()))
229         {
230             throw new IllegalArgumentException("The classes of the provided objects should be identical.");
231         }
232         // 00: priorities are equals
233         if (castor1.getPriority().equals(castor2.getPriority()))
234         {
235             // in the case of business items - just exception.
236             if (!(castor1 instanceof CastorSectionType))
237             {
238                 throw new JPConfigException("The elements with identical pathes can not be merged - scope [" +
239                     castor1.getScope() + "], name [" + castor1.getName() + "], priority [" + castor1.getPriority() + "]; class name " +
240                     "is [" + castor1.getClass().getName() + "]");
241             }
242             // we need to apply overriding depth feature to here
243             else
244             {
245                 // 00:b exception should be thrown - two identical pathes (including priotities)
246                 if (OverridingDepth.equals(
247                     com.sourceforge.jpatterns.core.configuration.PropertiesProvider.OverridingDepths.OVERRIDING_LEVEL_SECTION.toString()))
248                 {
249                     throw new JPConfigException("The elements with identical pathes can not be merged - scope [" +
250                         castor1.getScope() + "], name [" + castor1.getName() + "], priority [" + castor1.getPriority() + "]; class " +
251                         "name is [" + castor1.getClass().getName() + "]");
252                 }
253                 // 00:c no exception - we should merge the two items into the one (we won't check the items here - it would be done when
254                 // the items would be put to the specialized map)
255                 else if (OverridingDepth.equals(
256                     com.sourceforge.jpatterns.core.configuration.PropertiesProvider.OverridingDepths.OVERRIDING_LEVEL_ITEM.toString()))
257                 {
258                     final List<CastorNameScopePriorityType> items =
259                         CastorUtils.extractCastorNameScopePriorityTypeObjects((CastorSectionType) castor2);
260                     for (CastorNameScopePriorityType item : items)
261                     {
262                         // perform add functionality through the reflexion
263                         // note [zmicer]: very risky block still it is better then reflexion. Seems to be these three base classes limit
264                         // the scope of root section classes
265                         if (castor1 instanceof CastorConfigType)
266                         {
267                             if (!(item instanceof Item))
268                             {
269                                 throw new JPConfigException("The CastorNameScopePriorityType item should be of Item type in the " +
270                                     "case castor section object is of the type CastorConfigType");
271                             }
272                             ((CastorConfigType) castor1).addItem((Item) item);
273                         }
274                         else if (castor1 instanceof CastorFactoryType)
275                         {
276                             if (!(item instanceof Item))
277                             {
278                                 throw new JPConfigException("The CastorNameScopePriorityType item should be of Item type in the " +
279                                     "case castor section object is of the type CastorFactoryType");
280                             }
281                             ((CastorFactoryType) castor1).addItem((Item) item);
282                         }
283                         else if (castor1 instanceof CastorGroupType)
284                         {
285                             final CastorGroupTypeItem groupItem = CastorUtils.constructGroupItem(item);
286                             ((CastorGroupType) castor1).addCastorGroupTypeItem(groupItem);
287                         }
288                     }
289                     return castor1;
290                 }
291             }
292         }
293         else
294         {
295             int firstPriority = CastorUtils.getPriority(castor1);
296             int secondPriority = CastorUtils.getPriority(castor2);
297             boolean firstPrioritized = false;
298             boolean secondPrioritized = false;
299             if (castor1.getPriority().startsWith(JPConstants.PRIORITIZED_PRIOTITY_PREFIX))
300             {
301                 firstPrioritized = true;
302             }
303             if (castor2.getPriority().startsWith(JPConstants.PRIORITIZED_PRIOTITY_PREFIX))
304             {
305                 secondPrioritized = true;
306             }
307             // 00:d both prioritized or both not prioritized - just compare the priorities
308             if (firstPrioritized && secondPrioritized || (!firstPrioritized && !secondPrioritized))
309             {
310                 return (firstPriority > secondPriority) ? castor1 : castor2;
311             }
312             // 00:e first prioritized - check the property too
313             else if (firstPrioritized)
314             {
315                 if (OverrideNotDependingOnPriority)
316                 {
317                     return castor1;
318                 }
319                 else
320                 {
321                     return (firstPriority > secondPriority) ? castor1 : castor2;
322                 }
323             }
324             // 00:f second prioritized - check the property too
325             else
326             {
327                 if (OverrideNotDependingOnPriority)
328                 {
329                     return castor2;
330                 }
331                 else
332                 {
333                     return (firstPriority > secondPriority) ? castor1 : castor2;
334                 }
335             }
336         }
337         return null;
338     }
339 
340     /**
341      * Set the overriding depth. This method is necessary for the unit testing.
342      *
343      * @param depth the value we ned to set, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
344      */
345     protected static void setOverridingDepth(final String depth)
346     {
347         InputArgumentUtils.checkStrings(true, depth);
348         OverridingDepth = depth;
349     }
350 
351     /**
352      * @return overriding depth
353      */
354     protected static String getOverridingDepth()
355     {
356         return OverridingDepth;
357     }
358 
359     /**
360      * Set the OverrideNotDependingOnPriority static field with the new value. This method is necessary for unit testing.
361      *
362      * @param value new boolean value
363      */
364     protected static void setOverrideNotDependingOnPriority(final boolean value)
365     {
366         OverrideNotDependingOnPriority = value;
367     }
368 
369     /**
370      * @return the value of OverrideNotDependingOnPriority
371      */
372     protected static boolean getOverrideNotDependingOnPriority()
373     {
374         return OverrideNotDependingOnPriority;
375     }
376 }