1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.maintainablexml;
17
18 import org.apache.commons.beanutils.PropertyUtils;
19 import org.apache.commons.lang.StringUtils;
20 import org.springframework.core.io.AbstractResource;
21 import org.w3c.dom.Document;
22 import org.w3c.dom.Element;
23 import org.w3c.dom.Node;
24 import org.w3c.dom.NodeList;
25 import org.xml.sax.InputSource;
26
27 import javax.xml.parsers.DocumentBuilder;
28 import javax.xml.parsers.DocumentBuilderFactory;
29 import javax.xml.transform.OutputKeys;
30 import javax.xml.transform.Transformer;
31 import javax.xml.transform.TransformerFactory;
32 import javax.xml.transform.dom.DOMSource;
33 import javax.xml.transform.stream.StreamResult;
34 import javax.xml.xpath.XPath;
35 import javax.xml.xpath.XPathConstants;
36 import javax.xml.xpath.XPathExpression;
37 import javax.xml.xpath.XPathExpressionException;
38 import javax.xml.xpath.XPathFactory;
39 import java.io.StringReader;
40 import java.io.StringWriter;
41 import java.lang.reflect.InvocationTargetException;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47
48 public class MaintainableXMLConversionServiceImpl {
49
50 private static final String SERIALIZATION_ATTRIBUTE = "serialization";
51 private static final String CLASS_ATTRIBUTE = "class";
52 private static final String MAINTENANCE_ACTION_ELEMENT_NAME = "maintenanceAction";
53 private static final String OLD_MAINTAINABLE_OBJECT_ELEMENT_NAME = "oldMaintainableObject";
54 private static final String NEW_MAINTAINABLE_OBJECT_ELEMENT_NAME = "newMaintainableObject";
55
56 private Map<String, String> classNameRuleMap;
57 private Map<String, Map<String, String>> classPropertyRuleMap;
58 private Map<String, String> dateRuleMap;
59 public MaintainableXMLConversionServiceImpl() {
60 }
61
62 public String transformMaintainableXML(String xml) {
63 String beginning = StringUtils.substringBefore(xml, "<" + OLD_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">");
64 String oldMaintainableObjectXML = StringUtils.substringBetween(xml, "<" + OLD_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">", "</" + OLD_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">");
65 String newMaintainableObjectXML = StringUtils.substringBetween(xml, "<" + NEW_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">", "</" + NEW_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">");
66 String ending = StringUtils.substringAfter(xml, "</" + NEW_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">");
67
68 String convertedOldMaintainableObjectXML = transformSection(oldMaintainableObjectXML);
69 String convertedNewMaintainableObjectXML = transformSection(newMaintainableObjectXML);
70
71 String convertedXML = beginning +
72 "<" + OLD_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">" + convertedOldMaintainableObjectXML + "</" + OLD_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">" +
73 "<" + NEW_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">" + convertedNewMaintainableObjectXML + "</" + NEW_MAINTAINABLE_OBJECT_ELEMENT_NAME + ">" +
74 ending;
75 return convertedXML;
76 }
77
78 private String transformSection(String xml) {
79
80 String maintenanceAction = StringUtils.substringBetween(xml, "<" + MAINTENANCE_ACTION_ELEMENT_NAME + ">", "</" + MAINTENANCE_ACTION_ELEMENT_NAME + ">");
81 xml = StringUtils.substringBefore(xml, "<" + MAINTENANCE_ACTION_ELEMENT_NAME + ">");
82
83 try {
84 xml = upgradeBONotes(xml);
85 if (classNameRuleMap == null) {
86 setRuleMaps();
87 }
88
89 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
90 DocumentBuilder db = dbf.newDocumentBuilder();
91 Document document = db.parse(new InputSource(new StringReader(xml)));
92
93 removePersonObjects(document);
94
95 for(Node childNode = document.getFirstChild(); childNode != null;) {
96 Node nextChild = childNode.getNextSibling();
97 transformClassNode(document, childNode);
98 childNode = nextChild;
99 }
100
101 TransformerFactory transFactory = TransformerFactory.newInstance();
102 Transformer trans = transFactory.newTransformer();
103 trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
104 trans.setOutputProperty(OutputKeys.INDENT, "yes");
105
106 StringWriter writer = new StringWriter();
107 StreamResult result = new StreamResult(writer);
108 DOMSource source = new DOMSource(document);
109 trans.transform(source, result);
110 xml = writer.toString().replaceAll("(?m)^\\s+\\n", "");
111 } catch (Exception e) {
112 e.printStackTrace();
113 }
114
115 return xml + "<" + MAINTENANCE_ACTION_ELEMENT_NAME + ">" + maintenanceAction + "</" + MAINTENANCE_ACTION_ELEMENT_NAME + ">";
116 }
117
118
119
120
121
122
123
124
125 private String upgradeBONotes(String oldXML) throws Exception {
126
127 String notesXml = StringUtils.substringBetween(oldXML, "<boNotes>", "</boNotes>");
128 if (notesXml != null) {
129 notesXml = notesXml.replace("org.kuali.rice.kns.bo.Note", "org.kuali.rice.krad.bo.Note");
130 notesXml = "<org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl>\n"
131 + notesXml
132 + "\n</org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl>";
133 oldXML = oldXML.replaceFirst(">", ">\n<notes>\n" + notesXml + "\n</notes>");
134 }
135 return oldXML;
136 }
137
138 public void removePersonObjects( Document doc ) {
139 XPath xpath = XPathFactory.newInstance().newXPath();
140 XPathExpression personProperties = null;
141 try {
142 personProperties = xpath.compile("//*[@class='org.kuali.rice.kim.impl.identity.PersonImpl']");
143 NodeList matchingNodes = (NodeList)personProperties.evaluate( doc, XPathConstants.NODESET );
144 for(int i = 0; i < matchingNodes.getLength(); i++) {
145 Node tempNode = matchingNodes.item(i);
146 tempNode.getParentNode().removeChild(tempNode);
147 }
148 } catch (XPathExpressionException e) {
149 e.printStackTrace();
150 }
151 }
152
153 private void transformClassNode(Document document, Node node) throws ClassNotFoundException, XPathExpressionException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {
154 String className = node.getNodeName();
155 if(this.classNameRuleMap.containsKey(className)) {
156 String newClassName = this.classNameRuleMap.get(className);
157 document.renameNode(node, null, newClassName);
158 className = newClassName;
159 }
160 Class<?> dataObjectClass = Class.forName(className);
161 if(classPropertyRuleMap.containsKey(className)) {
162 transformNode(document, node, dataObjectClass, classPropertyRuleMap.get(className));
163 }
164 transformNode(document, node, dataObjectClass, classPropertyRuleMap.get("*"));
165 }
166
167 private void transformNode(Document document, Node node, Class<?> currentClass, Map<String, String> propertyMappings) throws ClassNotFoundException, XPathExpressionException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {
168 for(Node childNode = node.getFirstChild(); childNode != null;) {
169 Node nextChild = childNode.getNextSibling();
170 String propertyName = childNode.getNodeName();
171 if(childNode.hasAttributes()) {
172 XPath xpath = XPathFactory.newInstance().newXPath();
173 Node serializationAttribute = childNode.getAttributes().getNamedItem(SERIALIZATION_ATTRIBUTE);
174 if(serializationAttribute != null && StringUtils.equals(serializationAttribute.getNodeValue(), "custom")) {
175 Node classAttribute = childNode.getAttributes().getNamedItem(CLASS_ATTRIBUTE);
176 if(classAttribute != null && StringUtils.equals(classAttribute.getNodeValue(), "org.kuali.rice.kns.util.TypedArrayList")) {
177 ((Element)childNode).removeAttribute(SERIALIZATION_ATTRIBUTE);
178 ((Element)childNode).removeAttribute(CLASS_ATTRIBUTE);
179 XPathExpression listSizeExpression = xpath.compile("//" + propertyName + "/org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl/default/size/text()");
180 String size = (String)listSizeExpression.evaluate(childNode, XPathConstants.STRING);
181 List<Node> nodesToAdd = new ArrayList<Node>();
182 if(StringUtils.isNotBlank(size) && Integer.valueOf(size) > 0) {
183 XPathExpression listTypeExpression = xpath.compile("//" + propertyName + "/org.kuali.rice.kns.util.TypedArrayList/default/listObjectType/text()");
184 String listType = (String)listTypeExpression.evaluate(childNode, XPathConstants.STRING);
185 XPathExpression listContentsExpression = xpath.compile("//" + propertyName + "/org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl/" + listType);
186 NodeList listContents = (NodeList)listContentsExpression.evaluate(childNode, XPathConstants.NODESET);
187 for(int i = 0; i < listContents.getLength(); i++) {
188 Node tempNode = listContents.item(i);
189 transformClassNode(document, tempNode);
190 nodesToAdd.add(tempNode);
191 }
192 }
193 for(Node removeNode = childNode.getFirstChild(); removeNode != null;) {
194 Node nextRemoveNode = removeNode.getNextSibling();
195 childNode.removeChild(removeNode);
196 removeNode = nextRemoveNode;
197 }
198 for(Node nodeToAdd : nodesToAdd) {
199 childNode.appendChild(nodeToAdd);
200 }
201 } else {
202 ((Element)childNode).removeAttribute(SERIALIZATION_ATTRIBUTE);
203
204 XPathExpression mapContentsExpression = xpath.compile("//" + propertyName + "/map/string");
205 NodeList mapContents = (NodeList)mapContentsExpression.evaluate(childNode, XPathConstants.NODESET);
206 List<Node> nodesToAdd = new ArrayList<Node>();
207 if(mapContents.getLength() > 0 && mapContents.getLength() % 2 == 0) {
208 for(int i = 0; i < mapContents.getLength(); i++) {
209 Node keyNode = mapContents.item(i);
210 Node valueNode = mapContents.item(++i);
211 Node entryNode = document.createElement("entry");
212 entryNode.appendChild(keyNode);
213 entryNode.appendChild(valueNode);
214 nodesToAdd.add(entryNode);
215 }
216 }
217 for(Node removeNode = childNode.getFirstChild(); removeNode != null;) {
218 Node nextRemoveNode = removeNode.getNextSibling();
219 childNode.removeChild(removeNode);
220 removeNode = nextRemoveNode;
221 }
222 for(Node nodeToAdd : nodesToAdd) {
223 childNode.appendChild(nodeToAdd);
224 }
225 }
226 }
227 }
228 if(propertyMappings != null && propertyMappings.containsKey(propertyName)) {
229 String newPropertyName = propertyMappings.get(propertyName);
230 if(StringUtils.isNotBlank(newPropertyName)) {
231 document.renameNode(childNode, null, newPropertyName);
232 propertyName = newPropertyName;
233 } else {
234
235
236 node.removeChild(childNode);
237 childNode = nextChild;
238 continue;
239 }
240 }
241
242 if(dateRuleMap != null && dateRuleMap.containsKey(propertyName)) {
243 String newDateValue = dateRuleMap.get(propertyName);
244 if(StringUtils.isNotBlank(newDateValue)) {
245 if ( childNode.getTextContent().length() == 10 ) {
246 childNode.setTextContent( childNode.getTextContent() + " " + newDateValue );
247
248 }
249 }
250 }
251
252 if (currentClass != null) {
253 if (childNode.hasChildNodes() && !(Collection.class.isAssignableFrom(currentClass) || Map.class
254 .isAssignableFrom(currentClass))) {
255 Class<?> propertyClass = PropertyUtils.getPropertyType(currentClass.newInstance(), propertyName);
256 if (propertyClass != null && classPropertyRuleMap.containsKey(propertyClass.getName())) {
257 transformNode(document, childNode, propertyClass, this.classPropertyRuleMap.get(
258 propertyClass.getName()));
259 }
260 transformNode(document, childNode, propertyClass, classPropertyRuleMap.get("*"));
261 }
262 }
263 childNode = nextChild;
264 }
265 }
266
267
268
269
270 private void setRuleMaps() {
271 setupConfigurationMaps();
272 try {
273 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
274 DocumentBuilder db = dbf.newDocumentBuilder();
275
276 AbstractResource resource = null;
277
278 Document doc = db.parse(getClass().getResourceAsStream(
279 "/org/kuali/rice/devtools/krad/maintainablexml/MaintainableXMLUpgradeRules.xml"));
280
281 doc.getDocumentElement().normalize();
282 XPath xpath = XPathFactory.newInstance().newXPath();
283
284
285 XPathExpression exprClassNames = xpath.compile("//*[@name='maint_doc_classname_changes']/pattern");
286 NodeList classNamesList = (NodeList) exprClassNames.evaluate(doc, XPathConstants.NODESET);
287 for (int s = 0; s < classNamesList.getLength(); s++) {
288 String matchText = xpath.evaluate("match/text()", classNamesList.item(s));
289 String replaceText = xpath.evaluate("replacement/text()", classNamesList.item(s));
290 classNameRuleMap.put(matchText, replaceText);
291 }
292
293
294
295 XPathExpression exprClassProperties = xpath.compile(
296 "//*[@name='maint_doc_changed_class_properties']/pattern");
297 XPathExpression exprClassPropertiesPatterns = xpath.compile("pattern");
298 NodeList propertyClassList = (NodeList) exprClassProperties.evaluate(doc, XPathConstants.NODESET);
299 for (int s = 0; s < propertyClassList.getLength(); s++) {
300 String classText = xpath.evaluate("class/text()", propertyClassList.item(s));
301 Map<String, String> propertyRuleMap = new HashMap<String, String>();
302 NodeList classPropertiesPatterns = (NodeList) exprClassPropertiesPatterns.evaluate(
303 propertyClassList.item(s), XPathConstants.NODESET);
304 for (int c = 0; c < classPropertiesPatterns.getLength(); c++) {
305 String matchText = xpath.evaluate("match/text()", classPropertiesPatterns.item(c));
306 String replaceText = xpath.evaluate("replacement/text()", classPropertiesPatterns.item(c));
307 propertyRuleMap.put(matchText, replaceText);
308 }
309 classPropertyRuleMap.put(classText, propertyRuleMap);
310 }
311
312
313 XPathExpression dateFieldNames = xpath.compile("//*[@name='maint_doc_date_changes']/pattern");
314 NodeList DateNamesList = (NodeList) dateFieldNames.evaluate(doc, XPathConstants.NODESET);
315 for (int s = 0; s < DateNamesList.getLength(); s++) {
316 String matchText = xpath.evaluate("match/text()", DateNamesList.item(s));
317 String replaceText = xpath.evaluate("replacement/text()", DateNamesList.item(s));
318 dateRuleMap.put(matchText, replaceText);
319 }
320 } catch (Exception e) {
321 System.out.println("Error parsing rule xml file. Please check file. : " + e.getMessage());
322 e.printStackTrace();
323 }
324 }
325
326 private void setupConfigurationMaps() {
327 classNameRuleMap = new HashMap<String, String>();
328 classPropertyRuleMap = new HashMap<String, Map<String,String>>();
329 dateRuleMap = new HashMap<String, String>();
330
331
332 Map<String, String> defaultPropertyRules = new HashMap<String, String>();
333 defaultPropertyRules.put("boNotes", "");
334 defaultPropertyRules.put("autoIncrementSet", "");
335 classPropertyRuleMap.put("*", defaultPropertyRules);
336 }
337 }