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