View Javadoc
1   package org.codehaus.plexus.metadata.merge.support;
2   
3   /*
4    * The MIT License
5    *
6    * Copyright (c) 2006, The Codehaus
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   */
26  
27  import java.util.ArrayList;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Set;
31  
32  import org.codehaus.plexus.metadata.merge.MergeException;
33  import org.codehaus.plexus.metadata.merge.MergeStrategy;
34  import org.jdom2.Content;
35  import org.jdom2.Element;
36  
37  /**
38   * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
39   */
40  public abstract class AbstractMergeableElement extends AbstractMergeableSupport {
41      public AbstractMergeableElement(Element element) {
42          super(element);
43      }
44  
45      /**
46       * Detects if there was a conflict, that is the specified element was
47       * present in both dominant and recessive element-sets.
48       * <p>This delegates to
49       * {@link #isRecessiveElementInConflict(AbstractMergeableElement,List)}.</p>
50       *
51       * @param re      Recessive element.
52       * @param eltName Element name to test for.
53       * @return <code>true</code> if there was a conflict of element.
54       * @deprecated <em>use {@link #isRecessiveElementInConflict(AbstractMergeableElement,List)} instead.</em>
55       */
56      protected boolean isRecessiveElementInConflict(AbstractMergeableElement re, String eltName) {
57          // return (null != getChild (eltName) && null != re.getChild (eltName));
58          List l = new ArrayList();
59          l.add(eltName);
60          return isRecessiveElementInConflict(re, l);
61      }
62  
63      /**
64       * Detects if there was a conflict, that is the specified element was
65       * present in both dominant and recessive element-sets.
66       * <p>
67       * Use this to determine conflicts when the Dominant and Recessive element
68       * sets are keyed with Composite keys.</p>
69       * <p>For instance: <code>&lt;component&gt;</code> is keyed on
70       * <code>&lt;role&gt;</code> and <code>&lt;role-hint&gt;</code>.</p>
71       *
72       * @param re {@link AbstractMergeableElement}.
73       * @param eltNameList List of elements that will be checked for values in both dominant and recessive sets.
74       * @return true/false.
75       */
76      protected boolean isRecessiveElementInConflict(AbstractMergeableElement re, List eltNameList) {
77          // give opportunity to subclasses to provide any custom Composite keys
78          // for conflict checks.
79          eltNameList = getElementNamesForConflictResolution(eltNameList);
80  
81          if (null == eltNameList || eltNameList.size() == 0) {
82              return false;
83          }
84  
85          // assuming the elements will conflict.
86          for (Object anEltNameList : eltNameList) {
87              String eltName = (String) anEltNameList;
88              String dEltValue = getChildTextTrim(eltName);
89              String rEltValue = re.getChildTextTrim(eltName);
90              if (null == dEltValue || null == rEltValue || !dEltValue.equals(rEltValue)) {
91                  return false;
92              }
93          }
94          return true;
95      }
96  
97      /**
98       * Determines if the Element to be merged is to be sourced from Recessive
99       * Element set.
100      *
101      * @param re      Recessive element.
102      * @param eltName Element name to test for.
103      * @return true/false.
104      */
105     protected boolean mergeableElementComesFromRecessive(AbstractMergeableElement re, String eltName) {
106         return null == getChildText(eltName) && null != re.getChildText(eltName);
107     }
108 
109     /**
110      * Simply delegate to
111      *
112      * @see Mergeable#merge(Mergeable,org.codehaus.plexus.metadata.merge.MergeStrategy)
113      */
114     public void merge(Mergeable me, MergeStrategy strategy) throws MergeException {
115         // TODO set up a unit test for this!
116         strategy.apply(this, me);
117     }
118 
119     public void merge(Mergeable me) throws MergeException {
120         if (!isExpectedElementType(me)) {
121             // if (getLogger().isErrorEnabled)
122             //     getLogger().error ("Cannot Merge dissimilar elements. (Expected : '" + getClass ().getName () + "',
123             // found '" + me.getClass ().getName () + "')");
124             throw new MergeException("Cannot Merge dissimilar elements. " + "(Expected : '"
125                     + getClass().getName() + "', found '" + me.getClass().getName() + "')");
126         }
127         // recessive Component Element.
128         AbstractMergeableElement rce = (AbstractMergeableElement) me;
129 
130         Set allowedTags = new HashSet();
131 
132         for (int i = 0; i < getAllowedTags().length; i++) {
133             String tagName = getAllowedTags()[i].getTagName();
134 
135             allowedTags.add(tagName);
136 
137             List defaultConflictChecklist = new ArrayList();
138             defaultConflictChecklist.add(tagName);
139 
140             if (!isRecessiveElementInConflict(rce, defaultConflictChecklist)
141                     && mergeableElementComesFromRecessive(rce, tagName)) {
142                 this.addContent((Element) rce.getChild(tagName).clone());
143                 // else dominant wins in anycase!
144             } else if (getAllowedTags()[i].isMergeable()
145                     && isRecessiveElementInConflict(rce, defaultConflictChecklist)) {
146                 // this allows for merging multiple/list of elements.
147                 try {
148                     getAllowedTags()[i]
149                             .createMergeable(this.getChild(tagName))
150                             .merge(
151                                     getAllowedTags()[i].createMergeable(rce.getChild(tagName)),
152                                     getDefaultMergeStrategy());
153                 } catch (Exception e) {
154                     // TODO log to error
155                     throw new MergeException(
156                             "Unable to create Mergeable instance for tag " + "'" + getAllowedTags()[i] + "'.", e);
157                 }
158             }
159         }
160 
161         for (Object o : me.getElement().getChildren()) {
162             Element child = (Element) o;
163 
164             if (!allowedTags.contains(child.getName())) {
165                 // not yet merged, copy over
166                 element.addContent((Content) child.clone());
167             }
168         }
169     }
170 }