KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > xml > XMLMappingDescriptor


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.xml;
21
22 import java.util.Collection JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import javax.xml.parsers.DocumentBuilder JavaDoc;
28
29 import org.apache.cayenne.CayenneRuntimeException;
30 import org.apache.cayenne.Persistent;
31 import org.apache.cayenne.access.DataContext;
32 import org.apache.cayenne.reflect.PropertyUtils;
33 import org.w3c.dom.Attr JavaDoc;
34 import org.w3c.dom.Document JavaDoc;
35 import org.w3c.dom.Element JavaDoc;
36 import org.w3c.dom.NamedNodeMap JavaDoc;
37
38 /**
39  * A convenience class for dealing with the mapping file. This can encode and decode
40  * objects based upon the schema given by the map file.
41  *
42  * @author Kevin J. Menard, Jr.
43  * @since 1.2
44  */

45 final class XMLMappingDescriptor {
46
47     private SerializableEntity rootEntity;
48     private Map JavaDoc entities;
49     private DataContext dataContext;
50
51     /**
52      * Creates new XMLMappingDescriptor using a URL that points to the mapping file.
53      *
54      * @param mappingUrl A URL to the mapping file that specifies the mapping model.
55      * @throws CayenneRuntimeException
56      */

57     XMLMappingDescriptor(String JavaDoc mappingUrl) throws CayenneRuntimeException {
58
59         // Read in the mapping file.
60
DocumentBuilder JavaDoc builder = XMLUtil.newBuilder();
61
62         Document JavaDoc document;
63         try {
64             document = builder.parse(mappingUrl);
65         }
66         catch (Exception JavaDoc ex) {
67             throw new CayenneRuntimeException("Error parsing XML at " + mappingUrl, ex);
68         }
69
70         Element JavaDoc root = document.getDocumentElement();
71
72         if (!"model".equals(root.getNodeName())) {
73             throw new CayenneRuntimeException(
74                     "Root of the mapping model must be \"model\"");
75         }
76
77         Map JavaDoc entities = new HashMap JavaDoc();
78         Iterator JavaDoc it = XMLUtil.getChildren(root).iterator();
79         while (it.hasNext()) {
80             Element JavaDoc e = (Element JavaDoc) it.next();
81
82             SerializableEntity entity = new SerializableEntity(this, e);
83             String JavaDoc tag = e.getAttribute("xmlTag");
84             entities.put(tag, entity);
85
86             if (rootEntity == null) {
87                 rootEntity = entity;
88             }
89         }
90
91         this.entities = entities;
92     }
93
94     SerializableEntity getRootEntity() {
95         return rootEntity;
96     }
97
98     /**
99      * Decodes the supplied DOM document into an object.
100      *
101      * @param xml The DOM document containing the encoded object.
102      * @return The decoded object.
103      * @throws CayenneRuntimeException
104      */

105     Object JavaDoc decode(Element JavaDoc xml, DataContext dataContext) throws CayenneRuntimeException {
106
107         // TODO: Add an error check to make sure the mapping file actually is for this
108
// data file.
109

110         // Store a local copy of the data context.
111
this.dataContext = dataContext;
112         
113         // Create the object to be returned.
114
Object JavaDoc ret = createObject(rootEntity.getDescriptor(), xml);
115
116         // We want to read each value from the XML file and then set the corresponding
117
// property value in the object to be returned.
118
for (Iterator JavaDoc it = XMLUtil.getChildren(xml).iterator(); it.hasNext();) {
119             Element JavaDoc value = (Element JavaDoc) it.next();
120             decodeProperty(ret, rootEntity.getDescriptor(), value);
121         }
122
123         return ret;
124     }
125
126     /**
127      * Returns the entity XML block with the same "xmlTag" value as the passed in name.
128      *
129      * @param name The name of the entity to retrieve.
130      * @return The entity with "xmlTag" equal to the passed in name.
131      */

132     SerializableEntity getEntity(String JavaDoc name) {
133         return (SerializableEntity) entities.get(name);
134     }
135
136     /**
137      * Returns the property that is associated with the passed in XML tag.
138      *
139      * @param entityMapping The root to which the reference to find is relative to.
140      * @param propertyXmlTag The name of the entity.
141      * @return A name of the Java property mapped for the XML tag.
142      */

143     private String JavaDoc getPropertyMappingName(Element JavaDoc entityMapping, String JavaDoc propertyXmlTag) {
144         for (Iterator JavaDoc it = XMLUtil.getChildren(entityMapping).iterator(); it.hasNext();) {
145             Element JavaDoc propertyMapping = (Element JavaDoc) it.next();
146
147             if (propertyXmlTag.equals(propertyMapping.getAttribute("xmlTag"))) {
148                 return propertyMapping.getAttribute("name");
149             }
150         }
151
152         return null;
153     }
154
155     /**
156      * Decodes a property.
157      *
158      * @param object The object to be updated with the decoded property's value.
159      * @param entityMapping The entity block that contains the property mapping for the
160      * value.
161      * @param propertyData The encoded property.
162      * @throws CayenneRuntimeException
163      */

164     private void decodeProperty(Object JavaDoc object, Element JavaDoc entityMapping, Element JavaDoc propertyData)
165             throws CayenneRuntimeException {
166
167         String JavaDoc xmlTag = propertyData.getNodeName();
168         String JavaDoc propertyName = getPropertyMappingName(entityMapping, xmlTag);
169
170         // check unmapped data
171
if (propertyName == null) {
172             return;
173         }
174
175         SerializableEntity targetEntityMapping = getEntity(xmlTag);
176
177         // This is a "simple" encoded property.
178
if (targetEntityMapping == null) {
179             setProperty(object, propertyName, XMLUtil.getText(propertyData));
180         }
181         // nested entity property
182
else {
183
184             Object JavaDoc o = createObject(targetEntityMapping.getDescriptor(), propertyData);
185
186             // Decode each of the property's children, setting values in the newly
187
// created object.
188
Iterator JavaDoc it = XMLUtil.getChildren(propertyData).iterator();
189             while (it.hasNext()) {
190                 Element JavaDoc child = (Element JavaDoc) it.next();
191                 decodeProperty(o, targetEntityMapping.getDescriptor(), child);
192             }
193
194             setProperty(object, propertyName, o);
195         }
196     }
197
198     /**
199      * Sets decoded object property. If a property is of Collection type, an object is
200      * added to the collection.
201      */

202     private void setProperty(Object JavaDoc object, String JavaDoc propertyName, Object JavaDoc value) {
203
204         // attempt to first set as a simple property, on failure try collection...
205
// checking for collection first via 'PropertyUtils.getProperty' would throw an
206
// exception on valid simple properties that are settable but not gettable
207

208         try {
209             PropertyUtils.setProperty(object, propertyName, value);
210         }
211         catch (CayenneRuntimeException e) {
212             Object JavaDoc existingValue = PropertyUtils.getProperty(object, propertyName);
213             if (existingValue instanceof Collection JavaDoc && !(value instanceof Collection JavaDoc)) {
214                 ((Collection JavaDoc) existingValue).add(value);
215             }
216             else {
217                 throw e;
218             }
219         }
220     }
221
222     /**
223      * Instantiates a new object using information from entity mapping. Initializes all
224      * properties that exist as 'objectData' attributes. Wraps all exceptions in
225      * CayenneRuntimeException.
226      *
227      * @param entityMapping Element that describes object to XML mapping.
228      * @return The newly created object.
229      * @throws CayenneRuntimeException
230      */

231     private Object JavaDoc createObject(Element JavaDoc entityMapping, Element JavaDoc objectData) {
232         String JavaDoc className = entityMapping.getAttribute("name");
233
234         Object JavaDoc object;
235         try {
236             object = Class.forName(
237                     className,
238                     true,
239                     Thread.currentThread().getContextClassLoader()).newInstance();
240         }
241         catch (Exception JavaDoc ex) {
242             throw new CayenneRuntimeException("Error creating instance of class "
243                     + className, ex);
244         }
245         
246         // If a data context has been supplied by the user, then register the data object with the context.
247
if ((null != dataContext) && (object instanceof Persistent)) {
248             dataContext.registerNewObject((Persistent) object);
249         }
250
251         NamedNodeMap JavaDoc attributes = objectData.getAttributes();
252         for (int i = 0; i < attributes.getLength(); i++) {
253             Attr JavaDoc attribute = (Attr JavaDoc) attributes.item(i);
254             String JavaDoc propertyName = getPropertyMappingName(entityMapping, attribute
255                     .getName());
256
257             if (propertyName != null) {
258                 PropertyUtils.setProperty(object, propertyName, attribute.getValue());
259             }
260         }
261
262         return object;
263     }
264 }
265
Popular Tags