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: DBLoader.java
13   * Created: 2007-01-04
14   * Author: apohllo
15   * $Id: DBLoader.java 2232 2009-01-04 22:59:53Z apohllo $
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.lang.reflect.Type;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.SortedMap;
29  
30  import pl.edu.agh.cast.model.mapper.AttributeLoader;
31  import pl.edu.agh.cast.model.mapper.Direction;
32  import pl.edu.agh.cast.model.mapper.Link;
33  import pl.edu.agh.cast.model.mapper.LinkLoader;
34  import pl.edu.agh.cast.model.mapper.Mappable;
35  import pl.edu.agh.cast.model.mapper.Mapper;
36  import pl.edu.agh.cast.model.mapper.Node;
37  import pl.edu.agh.cast.model.mapper.annotation.MapAttribute;
38  import pl.edu.agh.cast.model.mapper.annotation.MapLink;
39  import pl.edu.agh.cast.model.mapper.annotation.Mapping;
40  
41  /**
42   * The loader which uses relational data base to store objects.
43   *
44   * @author AGH CAST Team
45   *
46   */
47  public class DBLoader extends AbstractLoader {
48  
49  	/**
50       *
51       */
52  	private static final String SET_PREFIX = "set";
53  
54  	/**
55  	 * The parameterized constructor.
56  	 *
57  	 * @param objectCache
58  	 *            The object cache used for referential integrity.
59  	 * @param klass
60  	 *            The class of the object to load
61  	 * @param typeMap2
62  	 *            The type map
63  	 * @param conditions
64  	 *            The conditions the object must satisfy to be loaded.
65  	 */
66  	public DBLoader(Map<Node, Mappable> objectCache, Class<Mappable> klass, Map<String, Class> typeMap2,
67  	        SortedMap<String, Object> conditions) {
68  		super(objectCache, klass, typeMap2, conditions);
69  	}
70  
71  	/**
72  	 *
73  	 * {@inheritDoc}
74  	 *
75  	 * @see pl.edu.agh.cast.model.mapper.Loader#find()
76  	 */
77  	public List<Mappable> find() {
78  		if (!Mapper.isDBPresent()) {
79  			log.warn("DB is not initialized"); //$NON-NLS-1$
80  			return new LinkedList<Mappable>();
81  		}
82  
83  		Mapping mapping = klass.getAnnotation(Mapping.class);
84  		if (mapping == null) {
85  			throw new IllegalArgumentException("Class " + klass //$NON-NLS-1$
86  			        + " is not mapped to metamodel"); //$NON-NLS-1$
87  		}
88  		if (typeMap == null) {
89  			typeMap = new HashMap<String, Class>();
90  		}
91  		String typeName = Helper.mappedTypeName(klass);
92  		if (typeMap.get(typeName) == null) {
93  			typeMap.put(typeName, klass);
94  		}
95  		try {
96  			List<Node> nodes = null;
97  			if (conditions == null) {
98  				nodes = Node.find(typeName);
99  			} else {
100 				nodes = Node.find(typeName, conditions);
101 			}
102 			List<Mappable> result = new LinkedList<Mappable>();
103 
104 			for (Node node : nodes) {
105 				result.add(load(node));
106 			}
107 			return result;
108 		} catch (Exception ex) {
109 			ex.printStackTrace();
110 		}
111 		return Collections.emptyList();
112 	}
113 
114 	/**
115 	 * Load model object mapped to the node passed as the argument. The loading process is recursive.
116 	 *
117 	 * @param node
118 	 *            The metamodel node
119 	 * @return Model object
120 	 * @throws Exception
121 	 */
122 	private Mappable load(Node node) throws Exception {
123 		Mappable cached = objectCache.get(node);
124 		Class<Mappable> klass = Helper.mappedClass(node.getTypeName(), typeMap);
125 
126 		if (cached != null && cached.getClass().equals(klass)) {
127 			return cached;
128 		}
129 
130 		Constructor<Mappable> constructor = Helper.getConstructor(klass);
131 		Mappable newInstance = constructor.newInstance((Object[])null);
132 		newInstance.setMid(node.getId());
133 		objectCache.put(node, newInstance);
134 		loadAttributes(node, newInstance, klass);
135 		loadLinks(node, newInstance, klass);
136 		return newInstance;
137 	}
138 
139 	/**
140 	 * Load links of given node
141 	 *
142 	 * @param node
143 	 *            Node whos attributes are loaded
144 	 * @param object
145 	 *            The object which owns loade links
146 	 * @param klass
147 	 *            The class of the loaded node
148 	 * @throws Exception
149 	 */
150 	private void loadLinks(Node node, Mappable object, Class<Mappable> klass) throws Exception {
151 		if (Helper.isSuperMapped(klass)) {
152 			loadLinks(node, object, (Class<Mappable>)klass.getSuperclass());
153 		}
154 
155 		// System.out.println("Loading... " + klass.getSimpleName() +" " +
156 		// object);
157 		for (LinkEntry entry : Helper.getMappedLinks(klass)) {
158 
159 			MapLink linkMapping = entry.getAnnotation();
160 			Direction direction = linkMapping.direction();
161 
162 			// node.reloadLinks();
163 			List<Link> links = node.links(linkMapping.name(), direction);
164 			String setterName = null;
165 			// rebuild all objects which are at the opposite site of the
166 			// link
167 			List<Mappable> linkedElements = new LinkedList<Mappable>();
168 			for (Link link : links) {
169 				if (direction == Direction.SRC) {
170 					// on this side the setter name must be the same for all
171 					// linked elements, so we can set the setter name once
172 					if (setterName == null) {
173 						setterName = SET_PREFIX + link.getSrcAccessor(); //$NON-NLS-1$
174 					}
175 					// load the object on the opposit side
176 					linkedElements.add(load(link.getDst()));
177 				} else {
178 					if (setterName == null) {
179 						setterName = SET_PREFIX + link.getDstAccessor(); //$NON-NLS-1$
180 					}
181 					linkedElements.add(load(link.getSrc()));
182 				}
183 			}
184 			if (linkMapping.load().equals(Object.class)) {
185 				// default loading mechanizm
186 				if (linkedElements.size() == 0) {
187 					// no linked elements found
188 
189 					// TODO introduce obligatory links (?)
190 					// if(!linkMapping.collection())
191 					// throw new RuntimeException("No value for singular
192 					// link "+
193 					// linkMapping.name() + " for " + klass +
194 					// " [" + node.getId() + "]");
195 					// }
196 				} else {
197 					if (setterName == null) {
198 						throw new RuntimeException("Metamodel error: missing link setter for " //$NON-NLS-1$
199 						        + "link " + linkMapping.name() //$NON-NLS-1$
200 						        + " for " + klass + " [" //$NON-NLS-1$ //$NON-NLS-2$
201 						        + node.getId() + "]"); //$NON-NLS-1$
202 					}
203 					Type klass2 = entry.getMethod().getReturnType();
204 					if (klass2 != null && klass2.equals(List.class)) {
205 						Method setter = object.getClass().getMethod(setterName, List.class);
206 						setter.invoke(object, new Object[] { linkedElements });
207 					} else {
208 						if (linkedElements.size() > 1) {
209 							throw new RuntimeException("Too many values for singular link " //$NON-NLS-1$
210 							        + linkMapping.name() + " for " //$NON-NLS-1$
211 							        + klass + " [" + node.getId() //$NON-NLS-1$
212 							        + "]"); //$NON-NLS-1$
213 						}
214 						Object linkedElement = linkedElements.get(0);
215 						Method setter = Helper.getSetter(object, linkedElement, setterName);
216 						setter.invoke(object, new Object[] { linkedElement });
217 					}
218 				}
219 			} else {
220 				Constructor cons = linkMapping.load().getConstructor(new Class[] { klass });
221 				LinkLoader loader = (LinkLoader)cons.newInstance(new Object[] { object });
222 				loader.loadLink(linkedElements);
223 			}
224 		}
225 	}
226 
227 	/**
228 	 * Load attributes of given node
229 	 *
230 	 * @param node
231 	 *            Node whos links are loaded
232 	 * @param object
233 	 *            The object which owns loaded attributes
234 	 * @param The
235 	 *            type map
236 	 * @see pl.edu.agh.cast.model.base.metamodel.annotation.Mapping
237 	 * @param klass
238 	 *            The class of the loaded node
239 	 * @throws Exception
240 	 */
241 	private void loadAttributes(Node node, Mappable object, Class<Mappable> klass) throws Exception {
242 		if (Helper.isSuperMapped(klass)) {
243 			loadAttributes(node, object, (Class<Mappable>)klass.getSuperclass());
244 		}
245 
246 		for (Method method : Helper.mappedAttributeMethods(klass)) {
247 			MapAttribute attributeMapping = method.getAnnotation(MapAttribute.class);
248 			String attributeName = attributeMapping.name();
249 			if (attributeName.equals("")) { //$NON-NLS-1$
250 				attributeName = method.getName();
251 			}
252 			try {
253 				List<Link> links = node.links(attributeName);
254 				if (attributeMapping.load().equals(Object.class)) {
255 					// default implementation
256 					// TODO array attributes
257 					if (links.size() > 1) {
258 						throw new RuntimeException("Too many values for attribute " //$NON-NLS-1$
259 						        + attributeName + " for " + klass + "[" //$NON-NLS-1$ //$NON-NLS-2$
260 						        + node.getId() + "]"); //$NON-NLS-1$
261 					} else if (links.size() != 1) {
262 						// TODO set to null by default?
263 						// add flag to the annotation indicating if the
264 						// value may be missing at initialization
265 						throw new RuntimeException("Missing value for attribute " + attributeName //$NON-NLS-1$
266 						        + " for " + klass + "[" + node.getId() //$NON-NLS-1$ //$NON-NLS-2$
267 						        + "]"); //$NON-NLS-1$
268 					} else {
269 						String setterName = SET_PREFIX + method.getName().substring(3);
270 						Method setterMethod = klass.getMethod(setterName, method.getReturnType());
271 						Object value = links.get(0).getSrc().getValue();
272 						try {
273 							setterMethod.invoke(object, new Object[] { value });
274 						} catch (IllegalArgumentException ex) {
275 							if (value instanceof Long) {
276 								Integer intValue = ((Long)value).intValue();
277 								setterMethod.invoke(object, new Object[] { intValue });
278 							}
279 						}
280 					}
281 				} else {
282 					Constructor cons = attributeMapping.load().getConstructor(new Class[] { klass });
283 					AttributeLoader loader = (AttributeLoader)cons.newInstance(new Object[] { object });
284 					loader.loadAttribute(node, links);
285 				}
286 			} catch (NoSuchMethodException e) {
287 				throw new RuntimeException("Missing setter for attribute " //$NON-NLS-1$
288 				        + attributeName + " for " + klass, e); //$NON-NLS-1$
289 			}
290 		}
291 	}
292 
293 }