001 /*
002 * JBoss, Home of Professional Open Source.
003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004 * as indicated by the @author tags. See the copyright.txt file in the
005 * distribution for a full listing of individual contributors.
006 *
007 * This is free software; you can redistribute it and/or modify it
008 * under the terms of the GNU Lesser General Public License as
009 * published by the Free Software Foundation; either version 2.1 of
010 * the License, or (at your option) any later version.
011 *
012 * This software is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this software; if not, write to the Free
019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021 */
022 package org.jboss.dna.connector.federation.merge.strategy;
023
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.UUID;
027 import net.jcip.annotations.ThreadSafe;
028 import org.jboss.dna.connector.federation.contribution.Contribution;
029 import org.jboss.dna.connector.federation.merge.FederatedNode;
030 import org.jboss.dna.connector.federation.merge.MergePlan;
031 import org.jboss.dna.graph.DnaLexicon;
032 import org.jboss.dna.graph.ExecutionContext;
033 import org.jboss.dna.graph.Location;
034 import org.jboss.dna.graph.properties.Path;
035 import org.jboss.dna.graph.properties.PathFactory;
036 import org.jboss.dna.graph.properties.Property;
037 import org.jboss.dna.graph.properties.ValueFormatException;
038
039 /**
040 * A merge strategy that is optimized for merging when there is a single contribution.
041 *
042 * @author Randall Hauch
043 */
044 @ThreadSafe
045 public class OneContributionMergeStrategy implements MergeStrategy {
046
047 /**
048 * {@inheritDoc}
049 * <p>
050 * This method only uses the one and only one non-null {@link Contribution} in the <code>contributions</code>.
051 * </p>
052 *
053 * @see org.jboss.dna.connector.federation.merge.strategy.MergeStrategy#merge(org.jboss.dna.connector.federation.merge.FederatedNode,
054 * java.util.List, org.jboss.dna.graph.ExecutionContext)
055 */
056 public void merge( FederatedNode federatedNode,
057 List<Contribution> contributions,
058 ExecutionContext context ) {
059 assert federatedNode != null;
060 assert context != null;
061 assert contributions != null;
062 assert contributions.size() > 0;
063 Contribution contribution = contributions.get(0);
064 assert contribution != null;
065 final PathFactory pathFactory = context.getValueFactories().getPathFactory();
066 final Location location = federatedNode.getActualLocationOfNode();
067
068 // Copy the children ...
069 Iterator<Location> childIterator = contribution.getChildren();
070 while (childIterator.hasNext()) {
071 Location child = translateChildFromSourceToRepository(pathFactory, location, childIterator.next());
072 federatedNode.addChild(child);
073 }
074
075 // Copy the properties ...
076 Property uuidProperty = null;
077 Property dnaUuidProperty = null;
078 Iterator<Property> propertyIterator = contribution.getProperties();
079 while (propertyIterator.hasNext()) {
080 Property property = propertyIterator.next();
081 federatedNode.addProperty(property);
082 if (property.isSingle()) {
083 if (property.getName().equals(DnaLexicon.UUID) && hasUuidValue(context, property)) {
084 dnaUuidProperty = property;
085 } else if (property.getName().getLocalName().equals("uuid") && hasUuidValue(context, property)) {
086 uuidProperty = property;
087 }
088 }
089 }
090 if (dnaUuidProperty != null) uuidProperty = dnaUuidProperty; // use "dna:uuid" if there is one
091
092 // Look for the UUID property on the properties, and update the federated node ...
093 if (uuidProperty != null && !uuidProperty.isEmpty()) {
094 UUID uuid = context.getValueFactories().getUuidFactory().create(uuidProperty.getValues().next());
095 federatedNode.setUuid(uuid);
096 if (dnaUuidProperty == null) {
097 uuidProperty = context.getPropertyFactory().create(DnaLexicon.UUID, uuid); // Use the "dna:uuid" name
098 }
099 federatedNode.setActualLocationOfNode(federatedNode.getActualLocationOfNode().with(uuidProperty));
100 } else {
101 // See if there is a UUID property on the location and update the federated node with it...
102 uuidProperty = federatedNode.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID);
103 if (uuidProperty == null || uuidProperty.isEmpty()) {
104 // Generate a new UUID property and add to the node ...
105 UUID uuid = federatedNode.getUuid();
106 if (uuid == null) {
107 uuid = context.getValueFactories().getUuidFactory().create();
108 federatedNode.setUuid(uuid);
109 }
110 uuidProperty = context.getPropertyFactory().create(DnaLexicon.UUID, uuid);
111 }
112 // Set the UUID as a property ...
113 federatedNode.addProperty(uuidProperty);
114 }
115
116 // Assign the merge plan ...
117 MergePlan mergePlan = MergePlan.create(contributions);
118 federatedNode.setMergePlan(mergePlan);
119 }
120
121 private boolean hasUuidValue( ExecutionContext context,
122 Property property ) {
123 assert property.isSingle();
124 try {
125 context.getValueFactories().getUuidFactory().create(property.getValues().next());
126 return true;
127 } catch (ValueFormatException e) {
128 return false;
129 }
130 }
131
132 /**
133 * Utility method to translate the list of locations of the children so that the locations all are correctly relative to
134 * parent location of the federated node.
135 *
136 * @param factory the path factory
137 * @param parent the parent of the child
138 * @param childInSource the child to be translated, with a source-specific location
139 * @return the list of locations of each child
140 */
141 protected Location translateChildFromSourceToRepository( PathFactory factory,
142 Location parent,
143 Location childInSource ) {
144 // Convert the locations of the children (relative to the source) to be relative to this node
145 Path parentPath = parent.getPath();
146 if (parentPath == null) return childInSource;
147 Path newPath = factory.create(parentPath, childInSource.getPath().getLastSegment());
148 return childInSource.with(newPath);
149 }
150 }