View Javadoc

1   /*
2    * This file is a part of CAST project.
3    * (c) Copyright 2007, AGH University of Science & Technology
4    * https://caribou.iisg.agh.edu.pl/trac/cast
5    *
6    * Licensed under the Eclipse Public License, Version 1.0 (the "License").
7    * You may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * http://www.eclipse.org/legal/epl-v10.html
10   */
11  /*
12   * File: ModelParser.java
13   * Created: 2008-07-15
14   * Author: apohllo
15   * $Id: ModelParser.java 2335 2009-01-13 16:40:16Z entrop $
16   */
17  
18  package pl.edu.agh.cast.model.mapper.internal;
19  
20  import java.lang.reflect.Constructor;
21  import java.lang.reflect.Method;
22  import java.util.Date;
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import org.xml.sax.Attributes;
27  import org.xml.sax.SAXException;
28  import org.xml.sax.helpers.DefaultHandler;
29  
30  import pl.edu.agh.cast.model.base.IDataSet;
31  import pl.edu.agh.cast.model.base.IEntity;
32  import pl.edu.agh.cast.model.base.IModel;
33  import pl.edu.agh.cast.model.base.IRelation;
34  import pl.edu.agh.cast.model.mapper.Mappable;
35  
36  /**
37   * The parser of the XML representation of the model.
38   *
39   * @author AGH CAST Team
40   */
41  class ModelParser extends DefaultHandler {
42  	private static final String NULL = "null"; //$NON-NLS-1$
43  
44  	private static final String INT_TYPE = "int"; //$NON-NLS-1$
45  
46  	private static final String DOUBLE_TYPE = "double"; //$NON-NLS-1$
47  
48  	private static final String BOOLEAN_TYPE = "boolean"; //$NON-NLS-1$
49  
50  	private static final String LONG_TYPE = "long"; //$NON-NLS-1$
51  
52  	private static final String SETTER_PREFIX = "set"; //$NON-NLS-1$
53  
54  	/**
55  	 *
56  	 */
57  	private Map<Long, Mappable> cache = new HashMap<Long, Mappable>();
58  
59  	private IDataSet dataSet;
60  
61  	private Mappable mappable;
62  
63  	private String mappableQName;
64  
65  	private Method setterMethod;
66  
67  	private Class returnType;
68  
69  	private IModel model;
70  
71  	private Map<String, Class> typeMap;
72  
73  	private StringBuilder contentBuffer = new StringBuilder();
74  
75  	public ModelParser(IModel newModel, Map<String, Class> newTypeMap) {
76  		model = newModel;
77  		typeMap = newTypeMap;
78  	}
79  
80  	/**
81  	 * Parse next element
82  	 *
83  	 * There are elements of three type:
84  	 * <ul>
85  	 * <li>root, which is simply discarded
86  	 * <li>mapped element, which has some attributes
87  	 * <li>attribute, which is attached to the mapped element
88  	 * </ul>
89  	 *
90  	 * @throws SAXException
91  	 */
92  	@Override
93  	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
94  		// skip the root node
95  		if (qName.equals(XMLSaver.ROOT)) {
96  			if (model.getId() == null) {
97  				model.setId(attributes.getValue(XMLSaver.ID));
98  			}
99  			return;
100 		}
101 		try {
102 			if (mappableQName == null) {
103 				// we are outside of XML node denoting model element - this is beginning of it
104 				Class<Mappable> klass = Helper.mappedClass(qName, typeMap);
105 				Constructor<Mappable> constructor = Helper.getConstructor(klass);
106 				mappable = constructor.newInstance((Object[])null);
107 				if (mappable instanceof IRelation) {
108 					IRelation relation = (IRelation)mappable;
109 					IEntity source = (IEntity)cache.get(Long.parseLong(attributes.getValue(XMLSaver.SOURCE)));
110 					if (source == null) {
111 						throw new SAXException("Relation references entity which was not yet " + //$NON-NLS-1$
112 						        "parsed. Data set: " + dataSet); //$NON-NLS-1$
113 					}
114 					relation.setSource(source);
115 					source.addRelation(relation);
116 					IEntity target = (IEntity)cache.get(Long.parseLong(attributes.getValue(XMLSaver.TARGET)));
117 					if (target == null) {
118 						throw new SAXException("Relation references entity which was not yet " + //$NON-NLS-1$
119 						        "parsed. Data set: " + dataSet); //$NON-NLS-1$
120 					}
121 					relation.setTarget(target);
122 					target.addRelation(relation);
123 				} else if (mappable instanceof IDataSet) {
124 					dataSet = (IDataSet)mappable;
125 					model.addDataSet(dataSet);
126 					dataSet.setModel(model);
127 				}
128 				mappableQName = qName;
129 				mappable.setMid(Long.parseLong(attributes.getValue(XMLSaver.ID)));
130 				cache.put(mappable.getMid(), mappable);
131 			} else {
132 				// we are inside of XML node denoting model element - this is an attribute
133 				String setterName = SETTER_PREFIX + qName.substring(3);
134 				String typeName = attributes.getValue(XMLSaver.TYPE);
135 				if (typeName.equals(LONG_TYPE)) {
136 					returnType = long.class;
137 				} else if (typeName.equals(BOOLEAN_TYPE)) {
138 					returnType = boolean.class;
139 				} else if (typeName.equals(DOUBLE_TYPE)) {
140 					returnType = double.class;
141 				} else if (typeName.equals(INT_TYPE)) {
142 					returnType = int.class;
143 				} else {
144 					returnType = Class.forName(typeName);
145 				}
146 
147 				setterMethod = mappable.getClass().getMethod(setterName, returnType);
148 
149 			}
150 		} catch (Exception e) {
151 			e.printStackTrace();
152 			throw new SAXException(e);
153 		}
154 	}
155 
156 	/**
157 	 * Appends characters stored in the buffer to the character table
158 	 *
159 	 * @param ch
160 	 *            The character table.
161 	 * @param start
162 	 *            The lower index of the character table where the characters shoudl be stored.
163 	 * @param length
164 	 *            The number of characters to be stored.
165 	 */
166 	@Override
167 	public void characters(char[] ch, int start, int length) {
168 		if (setterMethod != null) {
169 			// attribute data
170 			contentBuffer.append(ch, start, length);
171 		}
172 	}
173 
174 	/**
175 	 * {@inheritDoc}
176 	 *
177 	 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
178 	 */
179 	@Override
180 	public void endElement(String uri, String localName, String qName) throws SAXException {
181 		if (setterMethod != null) {
182 			// attribute end tag
183 			Object value = null;
184 			String valueString = contentBuffer.toString();
185 			contentBuffer.setLength(0);
186 			try {
187 				if (valueString.equals(NULL)) {
188 					value = null;
189 				} else if (String.class.equals(returnType)) {
190 					value = valueString;
191 				} else if (Long.class.equals(returnType) || long.class.equals(returnType)) {
192 					value = Long.parseLong(valueString);
193 				} else if (Integer.class.equals(returnType) || int.class.equals(returnType)) {
194 					value = Integer.parseInt(valueString);
195 				} else if (Double.class.equals(returnType) || double.class.equals(returnType)) {
196 					value = Double.parseDouble(valueString);
197 				} else if (Date.class.equals(returnType)) {
198 					value = XMLSaver.DATE_FORMAT.parse(valueString);
199 				} else if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) {
200 					if (valueString.equals("true")) { //$NON-NLS-1$
201 						value = new Boolean(true);
202 					} else {
203 						value = new Boolean(false);
204 					}
205 				}
206 				setterMethod.invoke(mappable, new Object[] { value });
207 				setterMethod = null;
208 			} catch (Exception e) {
209 				throw new SAXException(e);
210 			}
211 		} else if (mappableQName != null) {
212 			// element end tag
213 			if (mappableQName.equals(qName)) {
214 				if (mappable instanceof IRelation) {
215 					// the relation must be added after all its attributes (especially date)
216 					// are set
217 					dataSet.addRelation((IRelation)mappable);
218 				}
219 				mappableQName = null;
220 				mappable = null;
221 			}
222 		}
223 	}
224 }