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