View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.common.dictionary.service.impl.old;
17  
18  import java.text.SimpleDateFormat;
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.xml.parsers.DocumentBuilder;
25  import javax.xml.parsers.DocumentBuilderFactory;
26  
27  import org.apache.log4j.Logger;
28  import org.kuali.student.common.dictionary.old.dto.CaseConstraint;
29  import org.kuali.student.common.dictionary.old.dto.ConstraintDescriptor;
30  import org.kuali.student.common.dictionary.old.dto.ConstraintSelector;
31  import org.kuali.student.common.dictionary.old.dto.Context;
32  import org.kuali.student.common.dictionary.old.dto.Field;
33  import org.kuali.student.common.dictionary.old.dto.FieldDescriptor;
34  import org.kuali.student.common.dictionary.old.dto.LookupConstraint;
35  import org.kuali.student.common.dictionary.old.dto.LookupKeyConstraint;
36  import org.kuali.student.common.dictionary.old.dto.ObjectStructure;
37  import org.kuali.student.common.dictionary.old.dto.OccursConstraint;
38  import org.kuali.student.common.dictionary.old.dto.RequireConstraint;
39  import org.kuali.student.common.dictionary.old.dto.SearchSelector;
40  import org.kuali.student.common.dictionary.old.dto.State;
41  import org.kuali.student.common.dictionary.old.dto.Type;
42  import org.kuali.student.common.dictionary.old.dto.TypeStateCaseConstraint;
43  import org.kuali.student.common.dictionary.old.dto.TypeStateWhenConstraint;
44  import org.kuali.student.common.dictionary.old.dto.ValidCharsConstraint;
45  import org.kuali.student.common.dictionary.old.dto.WhenConstraint;
46  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
47  import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
48  import org.springframework.beans.factory.xml.ParserContext;
49  import org.w3c.dom.Attr;
50  import org.w3c.dom.Document;
51  import org.w3c.dom.Element;
52  import org.w3c.dom.Node;
53  
54  /**
55   * @author Daniel Epstein
56   *
57   */
58  @Deprecated
59  public class DictionaryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{
60      
61      final Logger logger = Logger.getLogger(DictionaryBeanDefinitionParser.class);
62  
63  	//Resolves the tag name to an actual class
64      @Override
65      protected Class<?> getBeanClass(Element element) {
66  
67      	if (element.getLocalName().equals("case")){
68      		return CaseConstraint.class;
69  		}
70  		if (element.getLocalName().equals("constraintDescriptor")) {
71  			return ConstraintDescriptor.class;
72  		}
73  		if (element.getLocalName().equals("constraint")) {
74  			return ConstraintSelector.class;
75  		}
76  		if (element.getLocalName().equals("context")) {
77  			return Context.class;
78  		}
79  		if (element.getLocalName().equals("field")) {
80  			return Field.class;
81  		}
82  		if (element.getLocalName().equals("fieldDescriptor")) {
83  			return FieldDescriptor.class;
84  		}
85  		if (element.getLocalName().equals("lookup")) {
86  			return LookupConstraint.class;
87  		}
88  		if (element.getLocalName().equals("lookupKey")) {
89  			return LookupKeyConstraint.class;
90  		}
91  		if (element.getLocalName().equals("objectStructure")) {
92  			return ObjectStructure.class;
93  		}
94  		if (element.getLocalName().equals("occurs")) {
95  			return OccursConstraint.class;
96  		}
97  		if (element.getLocalName().equals("require")) {
98  			return RequireConstraint.class;
99  		}
100 		if (element.getLocalName().equals("search")) {
101 			return SearchSelector.class;
102 		}
103 		if (element.getLocalName().equals("state")) {
104 			return State.class;
105 		}
106 		if (element.getLocalName().equals("type")) {
107 			return Type.class;
108 		}
109 		if (element.getLocalName().equals("typeStateCase")) {
110 			return TypeStateCaseConstraint.class;
111 		}
112 		if (element.getLocalName().equals("typeStateWhen")) {
113 			return TypeStateWhenConstraint.class;
114 		}
115 		if (element.getLocalName().equals("when")) {
116 			return WhenConstraint.class;
117 		}
118 		if (element.getLocalName().equals("validChars")) {
119 			return ValidCharsConstraint.class;
120 		}
121     	
122 
123         return super.getBeanClass(element);
124     }
125 
126     @Override
127     protected void doParse(Element element, ParserContext pc, BeanDefinitionBuilder builder) {
128   	
129     	//Copy Attributes
130     	if(element.hasAttributes()){
131     		for(int i = 0;i<element.getAttributes().getLength();i++){
132         		Attr attr = (Attr) element.getAttributes().item(i);
133         		if("abstract".equals(attr.getName())){
134         			builder.setAbstract(true);
135         		}else if(!"id".equals(attr.getName())&&!"parent".equals(attr.getName())){
136         			builder.addPropertyValue(attr.getName(), attr.getValue());
137         		}
138     		}
139     	}
140     	
141 	    //Parse the children
142     	HashSet<String> visitedNodes = new HashSet<String>();
143         for(int i = 0;i<element.getChildNodes().getLength();i++){
144             Node node = element.getChildNodes().item(i);
145 
146             //We are only interested in child elements that have not been visited
147             if(Node.ELEMENT_NODE == node.getNodeType()){	              
148 	            
149             	//Get the local name minus the "Ref"
150             	String localName=node.getLocalName();
151             	if(localName.endsWith("Ref")){
152             		localName=localName.substring(0, localName.length()-"Ref".length());
153             	}
154             	if(!visitedNodes.contains(localName)){
155 	            	//Check if the child element belongs in a list
156 	            	if(isList(localName)){
157 	            		Element childList=getChildList(element,localName);
158 	                	visitedNodes.add(localName);
159 	                    List<?> refList = pc.getDelegate().parseListElement(childList, pc.getContainingBeanDefinition());
160 	                    if(refList!=null&&!refList.isEmpty()){
161 	                    	String fieldName=resolveFieldName(element.getLocalName(),localName);
162 	                    	builder.addPropertyValue(fieldName,refList);
163 	                    }
164 	                //Check if this is an attribute map
165 	            	}else if("attributes".equals(node.getLocalName())){
166 	            		Map<String,String> attributes = getAttributeMap((Element)node);
167 	            		builder.addPropertyValue(node.getLocalName(), attributes);
168 	                //Check if the child element is a Ref
169 	                }else if(node.getLocalName().endsWith("Ref")){
170 	                	if("objectStructureRef".equals(node.getLocalName())){
171 	                		builder.addPropertyValue("objectStructureRef", ((Element)node).getAttribute("bean"));
172 	                		//Add in the nested object too
173 	                		builder.addPropertyReference("objectStructure", ((Element)node).getAttribute("bean"));
174 	                	}else{
175 	                		builder.addPropertyReference(localName, ((Element)node).getAttribute("bean"));
176 	                	}
177 	               }else{
178 	            	    //Get the child of the child to see if we need to parse the nested node, or just set the text value
179 	                    Element childElement = getFirstChildElement(node);
180 	                    if(childElement!=null ||"search".equals(node.getLocalName())){
181 	                    	//Parse the nested Node
182 	                        Object childBean = pc.getDelegate().parsePropertySubElement((Element)node, pc.getContainingBeanDefinition());
183 	                        String fieldName=resolveFieldName(element.getLocalName(),node.getLocalName());
184 	                        builder.addPropertyValue(fieldName, childBean);
185 	                    }else{
186 	                    	
187 	                    	
188 	                   		//Set the text value
189 	                		String fieldName=resolveFieldName(element.getLocalName(),node.getLocalName());
190 
191 	                		if(Node.ELEMENT_NODE == node.getNodeType()&&"date".equals(((Element)node).getSchemaTypeInfo().getTypeName())){
192 	                			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
193 	                			try {
194 									builder.addPropertyValue(fieldName, df.parse(node.getTextContent()));
195 								} catch (Exception e) {
196 									logger.error("Cannot convert date, must be in format 'YYYY-MM-DD' :"+node.getTextContent(),e);
197 								}
198 	                		}else{
199 	                			builder.addPropertyValue(fieldName, node.getTextContent());
200 	                		}
201 	                    }
202 	                }
203 	            }
204 	        }
205         }
206     }
207 
208     
209     /**
210      * Parses attribute map from 
211      * &lt;attributes&gt;
212      * 	&lt;attribute key="attr1" value="attr2"/&gt;
213      * &lt;/attributes&gt;
214      * @param element
215      * @return map of attributes
216      */
217     private Map<String, String> getAttributeMap(Element element) {
218     	Map<String, String> attributes = new HashMap<String, String>();
219     	for(int i = 0;i<element.getChildNodes().getLength();i++){
220     		Node node = element.getChildNodes().item(i);
221     		if(Node.ELEMENT_NODE == node.getNodeType() && "attribute".equals(node.getLocalName())){
222     			String key = ((Element)node).getAttribute("key");
223     			String value = ((Element)node).getAttribute("value");
224     			attributes.put(key, value);
225     		}
226     	}
227 		return attributes;
228 	}
229 
230 	//This builds up a list of the child nodes so that the spring parseListElement can be used
231     //it also translates <fooRef> elements into straight spring <ref> elements
232     private Element getChildList(Element element, String localName) {
233     	try{
234     		//Create a new document to contain our list of elements
235 	    	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
236 	    	DocumentBuilder builder = dbf.newDocumentBuilder();
237 	    	Document doc = builder.newDocument();
238 	
239 	    	Element root = doc.createElement("listRoot");
240 	    	
241 	        for(int i = 0;i<element.getChildNodes().getLength();i++){
242 	            Node node = element.getChildNodes().item(i);
243 	            if(Node.ELEMENT_NODE == node.getNodeType() && localName.equals(node.getLocalName())){
244 	            	
245 	            	//Copy the node from the spring config to our list
246 	            	Node copied = doc.importNode(node, true);
247 	            	root.appendChild(copied);
248 	            }
249 	            if(Node.ELEMENT_NODE == node.getNodeType() && (localName+"Ref").equals(node.getLocalName())){
250 	            	
251 	            	//Create a new spring ref element and copy the bean attribute
252 	            	Element ref = doc.createElement("ref");
253 	            	ref.setAttribute("bean", ((Element)node).getAttribute("bean"));
254 	            	root.appendChild(ref);
255 	            }
256 	        }
257 	        
258 	    	return root;
259     	}catch(Exception e){
260     		logger.error("Exception occured: ", e);
261     	}
262     	return null;
263 	}
264 
265     //This is called to resolve tag names to field names based on the element and parent element local names
266 	private String resolveFieldName(String parentName, String nodeName) {
267 		if("constraint".equals(parentName)&&"case".equals(nodeName)){
268 			return "caseConstraint";
269 		}
270 		if("constraint".equals(parentName)&&"typeStateCase".equals(nodeName)){
271 			return "typeStateCaseConstraint";
272 		}
273 		if("constraint".equals(parentName)&&"lookup".equals(nodeName)){
274 			return "lookupConstraint";
275 		}
276 		if("constraint".equals(parentName)&&"occurs".equals(nodeName)){
277 			return "occursConstraint";
278 		}
279 		if("constraint".equals(parentName)&&"require".equals(nodeName)){
280 			return "requireConstraint";
281 		}
282 		if("case".equals(parentName)&&"when".equals(nodeName)){
283 			return "whenConstraint";
284 		}
285 
286 		return nodeName;
287 	}
288 
289 	//Gets the first child element
290 	private Element getFirstChildElement(Node node) {
291         for(int i = 0;i<node.getChildNodes().getLength();i++){
292             Node childNode = node.getChildNodes().item(i);
293             if(Node.ELEMENT_NODE == childNode.getNodeType()){
294                 return (Element) childNode;
295             }
296         }
297         return null;
298     }
299 
300 	//Returns true if the element should be part of a list
301     private boolean isList(String localName) {
302 
303         return "field".equals(localName)||
304         	   "case".equals(localName)||
305         	   "when".equals(localName)||
306         	   "lookup".equals(localName)||
307         	   "lookupKey".equals(localName)||
308         	   "occurs".equals(localName)||
309         	   "constraint".equals(localName)||
310         	   "type".equals(localName)||
311         	   "state".equals(localName)||
312         	   "require".equals(localName);
313     }
314 
315     //This makes use of the spring parent="" functionality 
316 	@Override
317 	protected String getParentName(Element element) {
318 		if(element.hasAttribute("parent")){
319             return element.getAttribute("parent");
320 		}
321 		return super.getParentName(element);
322 	}
323 
324 	//This means any bean without an id attribute gets one auto generated for it
325 	@Override
326 	protected boolean shouldGenerateIdAsFallback() {
327 		return true;
328 	}
329 	
330 }