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.Iterator;
30  import java.util.List;
31  import java.util.Set;
32  
33  import org.codehaus.plexus.metadata.merge.MergeException;
34  import org.codehaus.plexus.metadata.merge.MergeStrategy;
35  import org.jdom.Content;
36  import org.jdom.Element;
37  
38  /**
39   * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
40   * @version $Id$
41   */
42  public abstract class AbstractMergeableElement
43      extends AbstractMergeableSupport
44  {
45      public AbstractMergeableElement( Element element )
46      {
47          super( element );
48      }
49  
50      /**
51       * Detects if there was a conflict, that is the specified element was
52       * present in both dominant and recessive element-sets.
53       * <p/>
54       * This delegates to
55       * {@link #isRecessiveElementInConflict(AbstractMergeableElement,List)}.
56       *
57       * @param re      Recessive element.
58       * @param eltName Element name to test for.
59       * @return <code>true</code> if there was a conflict of element.
60       * @deprecated <em>use {@link #isRecessiveElementInConflict(AbstractMergeableElement,List)} instead.</em>
61       */
62      protected boolean isRecessiveElementInConflict( AbstractMergeableElement re, String eltName )
63      {
64          // return (null != getChild (eltName) && null != re.getChild (eltName));
65          List l = new ArrayList();
66          l.add( eltName );
67          return isRecessiveElementInConflict( re, l );
68      }
69  
70      /**
71       * Detects if there was a conflict, that is the specified element was
72       * present in both dominant and recessive element-sets.
73       * <p/>
74       * Use this to determine conflicts when the Dominant and Recessive element
75       * sets are keyed with Composite keys.<br>
76       * For instance: <code>&lt;component&gt;</code> is keyed on
77       * <code>&lt;role&gt;</code> and <code>&lt;role-hint&gt;</code>.
78       *
79       * @param re
80       * @param eltNameList List of elements that will be checked for values in both dominant and recessive sets.
81       * @return
82       */
83      protected boolean isRecessiveElementInConflict( AbstractMergeableElement re, List eltNameList )
84      {
85          // give opportunity to subclasses to provide any custom Composite keys
86          // for conflict checks.
87          eltNameList = getElementNamesForConflictResolution( eltNameList );
88  
89          if ( null == eltNameList || eltNameList.size() == 0 )
90          {
91              return false;
92          }
93  
94          // assuming the elements will conflict.
95          for (Object anEltNameList : eltNameList) {
96              String eltName = (String) anEltNameList;
97              String dEltValue = getChildTextTrim(eltName);
98              String rEltValue = re.getChildTextTrim(eltName);
99              if (null == dEltValue || null == rEltValue || !dEltValue.equals(rEltValue)) {
100                 return false;
101             }
102         }
103         return true;
104     }
105 
106     /**
107      * Determines if the Element to be merged is to be sourced from Recessive
108      * Element set.
109      *
110      * @param re      Recessive element.
111      * @param eltName Element name to test for.
112      * @return
113      */
114     protected boolean mergeableElementComesFromRecessive( AbstractMergeableElement re, String eltName )
115     {
116         return null == getChildText( eltName ) && null != re.getChildText( eltName );
117     }
118 
119     /**
120      * Simply delegate to
121      *
122      * @see Mergeable#merge(Mergeable,org.codehaus.plexus.metadata.merge.MergeStrategy)
123      */
124     public void merge( Mergeable me, MergeStrategy strategy )
125         throws MergeException
126     {
127         // TODO set up a unit test for this!
128         strategy.apply( this, me );
129     }
130 
131     public void merge( Mergeable me )
132         throws MergeException
133     {
134         if ( !isExpectedElementType( me ) )
135         {
136             // if (getLogger().isErrorEnabled)
137             //     getLogger().error ("Cannot Merge dissimilar elements. (Expected : '" + getClass ().getName () + "', found '" + me.getClass ().getName () + "')");
138             throw new MergeException( "Cannot Merge dissimilar elements. " + "(Expected : '" + getClass().getName() +
139                 "', found '" + me.getClass().getName() + "')" );
140         }
141         // recessive Component Element.
142         AbstractMergeableElement rce = (AbstractMergeableElement) me;
143 
144         Set allowedTags = new HashSet();
145 
146         for ( int i = 0; i < getAllowedTags().length; i++ )
147         {
148             String tagName = getAllowedTags()[i].getTagName();
149 
150             allowedTags.add( tagName );
151 
152             List defaultConflictChecklist = new ArrayList();
153             defaultConflictChecklist.add( tagName );
154 
155             if ( !isRecessiveElementInConflict( rce, defaultConflictChecklist ) &&
156                 mergeableElementComesFromRecessive( rce, tagName ) )
157             {
158                 this.addContent( (Element) rce.getChild( tagName ).clone() );
159                 // else dominant wins in anycase!
160             }
161             else
162             if ( getAllowedTags()[i].isMergeable() && isRecessiveElementInConflict( rce, defaultConflictChecklist ) )
163             {
164                 // this allows for merging multiple/list of elements.
165                 try
166                 {
167                     getAllowedTags()[i].createMergeable( this.getChild( tagName ) )
168                         .merge( getAllowedTags()[i].createMergeable( rce.getChild( tagName ) ),
169                                 getDefaultMergeStrategy() );
170                 }
171                 catch ( Exception e )
172                 {
173                     // TODO log to error
174                     throw new MergeException(
175                         "Unable to create Mergeable instance for tag " + "'" + getAllowedTags()[i] + "'.", e );
176                 }
177             }
178         }
179 
180         for (Object o : me.getElement().getChildren()) {
181             Element child = (Element) o;
182 
183             if (!allowedTags.contains(child.getName())) {
184                 // not yet merged, copy over
185                 element.addContent((Content) child.clone());
186             }
187         }
188 
189     }
190 
191 }