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><component></code> is keyed on
70 * <code><role></code> and <code><role-hint></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 }