View Javadoc
1   package org.argeo.people.core;
2   
3   import static java.util.Arrays.asList;
4   
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import javax.jcr.Node;
9   import javax.jcr.NodeIterator;
10  import javax.jcr.Property;
11  import javax.jcr.PropertyIterator;
12  import javax.jcr.PropertyType;
13  import javax.jcr.RepositoryException;
14  import javax.jcr.Value;
15  import javax.jcr.ValueFactory;
16  import javax.jcr.query.Query;
17  import javax.jcr.query.QueryManager;
18  
19  import org.argeo.connect.ConnectNames;
20  import org.argeo.connect.util.ConnectJcrUtils;
21  import org.argeo.people.ImportService;
22  import org.argeo.people.PeopleException;
23  import org.argeo.people.PeopleNames;
24  import org.argeo.people.PeopleService;
25  import org.argeo.people.PeopleTypes;
26  
27  /** Default implementation of the import service */
28  public class ImportServiceImpl implements ImportService, PeopleNames {
29  
30  	private final PeopleService peopleService;
31  
32  	public ImportServiceImpl(PeopleService peopleService) {
33  		this.peopleService = peopleService;
34  	}
35  
36  	// Filtered properties
37  	private final List<String> TECHNICAL_PROPERTIES = asList("jcr:uuid", "jcr:baseVersion", "jcr:isCheckedOut",
38  			"jcr:predecessors", "jcr:frozenUuid", "jcr:versionHistory", "jcr:frozenPrimaryType", "jcr:primaryType",
39  			"jcr:mixinTypes", "jcr:created", "jcr:createdBy", "jcr:lastModified", "jcr:lastModifiedBy");
40  
41  	private final List<String> TECHNICAL_NODES = asList("rep:policy");
42  
43  	public void mergeNodes(Node masterNode, Node slaveNode) throws RepositoryException {
44  		// current nodes
45  		PropertyIterator pit = slaveNode.getProperties();
46  		props: while (pit.hasNext()) {
47  			Property currProp = pit.nextProperty();
48  			if (TECHNICAL_PROPERTIES.contains(currProp.getName()))
49  				continue props;
50  
51  			Property masterCurrProp = null;
52  			if (masterNode.hasProperty(currProp.getName()))
53  				masterCurrProp = masterNode.getProperty(currProp.getName());
54  			mergeProperty(masterNode, masterCurrProp, currProp);
55  		}
56  
57  		NodeIterator nit = slaveNode.getNodes();
58  		nodes: while (nit.hasNext()) {
59  			Node currNode = nit.nextNode();
60  			if (TECHNICAL_NODES.contains(currNode.getName()))
61  				continue nodes;
62  			Node masterCurrChild = null;
63  			// TODO: this skips the additional external IDs from same source as
64  			// external ID node name is the source ID
65  			if (masterNode.hasNode(currNode.getName()))
66  				masterCurrChild = masterNode.getNode(currNode.getName());
67  			else
68  				masterCurrChild = masterNode.addNode(currNode.getName(), currNode.getPrimaryNodeType().getName());
69  			mergeNodes(masterCurrChild, currNode);
70  		}
71  
72  		// TODO merge mixin?
73  		if (slaveNode.hasProperty(ConnectNames.CONNECT_UID))
74  			mergeInternalReferences(masterNode, slaveNode);
75  
76  		if (slaveNode.isNodeType("mix:referenceable"))
77  			mergeJcrReferences(masterNode, slaveNode);
78  	}
79  
80  	protected void mergeJcrReferences(Node masterNode, Node slaveNode) throws RepositoryException {
81  		PropertyIterator pit = slaveNode.getReferences();
82  		while (pit.hasNext()) {
83  			Property ref = pit.nextProperty();
84  			Node referencing = ref.getParent();
85  			// checkCOStatusBeforeUpdate(referencing);
86  			if (ref.isMultiple()) {
87  				ConnectJcrUtils.removeRefFromMultiValuedProp(referencing, ref.getName(), slaveNode.getIdentifier());
88  				ConnectJcrUtils.addRefToMultiValuedProp(referencing, ref.getName(), masterNode);
89  			} else
90  				referencing.setProperty(ref.getName(), masterNode);
91  		}
92  	}
93  
94  	protected void mergeInternalReferences(Node masterNode, Node slaveNode) throws RepositoryException {
95  		NodeIterator nit = internalReferencing(slaveNode);
96  		String peopleUId = masterNode.getProperty(ConnectNames.CONNECT_UID).getString();
97  		while (nit.hasNext()) {
98  			Node referencing = nit.nextNode();
99  			// checkCOStatusBeforeUpdate(referencing);
100 			referencing.setProperty(PEOPLE_REF_UID, peopleUId);
101 		}
102 	}
103 
104 	protected NodeIterator internalReferencing(Node slaveNode) throws RepositoryException {
105 		String peopleUId = slaveNode.getProperty(ConnectNames.CONNECT_UID).getString();
106 		QueryManager qm = slaveNode.getSession().getWorkspace().getQueryManager();
107 		Query query = qm.createQuery("select * from [nt:base] as nodes where ISDESCENDANTNODE('"
108 				+ peopleService.getBaseRelPath(PeopleTypes.PEOPLE_ENTITY) + "') AND [" + PEOPLE_REF_UID + "]='"
109 				+ peopleUId + "'" + " ", Query.JCR_SQL2);
110 		return query.execute().getNodes();
111 	}
112 
113 	protected void mergeProperty(Node masterNode, Property masterProp, Property slaveProp) throws RepositoryException {
114 		if (slaveProp.isMultiple())
115 			mergeMultipleProperty(masterNode, masterProp, slaveProp);
116 		else if (masterProp == null) {
117 			masterNode.setProperty(slaveProp.getName(), slaveProp.getValue());
118 		}
119 		// TODO won't merge properties with empty values.
120 	}
121 
122 	private void mergeMultipleProperty(Node masterNode, Property masterProp, Property slaveProp)
123 			throws RepositoryException {
124 		Value[] slaveVals = slaveProp.getValues();
125 		if (masterProp == null) {
126 			masterNode.setProperty(slaveProp.getName(), slaveVals);
127 		} else {
128 			Value[] vals = masterProp.getValues();
129 			if (vals[0].getType() == PropertyType.STRING) {
130 				List<String> res = new ArrayList<String>();
131 				for (Value val : vals)
132 					res.add(val.getString());
133 				for (Value val : slaveVals) {
134 					String currStr = val.getString();
135 					if (!res.contains(currStr))
136 						res.add(currStr);
137 				}
138 				masterProp.setValue(res.toArray(new String[0]));
139 			} else if (vals[0].getType() == PropertyType.REFERENCE) {
140 				List<String> res = new ArrayList<String>();
141 				for (Value val : vals)
142 					res.add(val.getString());
143 				for (Value val : slaveVals) {
144 					String currStr = val.getString();
145 					if (!res.contains(currStr))
146 						res.add(currStr);
147 				}
148 				ValueFactory vFactory = masterNode.getSession().getValueFactory();
149 				int size = res.size();
150 				Value[] values = new Value[size];
151 				int i = 0;
152 				for (String id : res) {
153 					Value val = vFactory.createValue(id, PropertyType.REFERENCE);
154 					values[i++] = val;
155 				}
156 				masterNode.setProperty(slaveProp.getName(), values);
157 			} else {
158 				throw new PeopleException(
159 						"Unsupported multiple property type on property " + masterProp + "for node " + masterNode);
160 			}
161 		}
162 	}
163 }