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: DBSaver.java
13   * Created: 2008-01-04
14   * Author: apohllo
15   * $Id: DBSaver.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.sql.SQLException;
24  import java.util.List;
25  
26  import pl.edu.agh.cast.model.mapper.AttributeSaver;
27  import pl.edu.agh.cast.model.mapper.Direction;
28  import pl.edu.agh.cast.model.mapper.Link;
29  import pl.edu.agh.cast.model.mapper.Mappable;
30  import pl.edu.agh.cast.model.mapper.Mapper;
31  import pl.edu.agh.cast.model.mapper.Node;
32  import pl.edu.agh.cast.model.mapper.Priority;
33  import pl.edu.agh.cast.model.mapper.annotation.MapLink;
34  import pl.edu.agh.cast.model.mapper.annotation.Mapping;
35  
36  /**
37   * The saver implementation which uses relational data base to store objects.
38   *
39   * @author AGH CAST Team
40   *
41   */
42  public class DBSaver extends AbstractSaver {
43  
44  	/**
45  	 * The parameterized constructor.
46  	 *
47  	 * @param object
48  	 *            The object to save.
49  	 * @param force
50  	 *            Flag indicating that given object should be saved, no matter if it was saved earlier.
51  	 */
52  	public DBSaver(Mappable object, boolean force) {
53  		super(object, force);
54  	}
55  
56  	/**
57  	 *
58  	 * {@inheritDoc}
59  	 *
60  	 * @see pl.edu.agh.cast.model.mapper.Saver#save()
61  	 */
62  	public boolean save() {
63  		if (!Mapper.isDBPresent()) {
64  			log.warn("DB is not initialized"); //$NON-NLS-1$
65  			return false;
66  		}
67  		try {
68  			queue.addLast(primaryObject);
69  			visited.add(primaryObject);
70  			while (!queue.isEmpty()) {
71  				Mappable element = queue.removeFirst();
72  				Node node = internalSave(element);
73  				Class klass = element.getClass();
74  				saveLinks(element, node, klass);
75  				stored.add(element);
76  			}
77  
78  			return true;
79  		} catch (Exception e) {
80  			e.printStackTrace();
81  			return false;
82  		}
83  	}
84  
85  	private Node internalSave(Mappable object) throws Exception {
86  		Class<Mappable> klass = (Class<Mappable>)object.getClass();
87  		if (klass.isAnnotationPresent(Mapping.class)) {
88  			Node node;
89  			long id = object.getMid();
90  			if (id == 0) {
91  				node = new Node(Helper.mappedTypeName(klass));
92  				if (!node.save()) {
93  					throw new RuntimeException("Node for object " + object //$NON-NLS-1$
94  					        + " not saved."); //$NON-NLS-1$
95  				}
96  				object.setMid(node.getId());
97  				nodes.put(node.getId(), node);
98  			} else {
99  				node = nodes.get(id);
100 				if (node == null) {
101 					node = Node.find(id);
102 				}
103 			}
104 			saveAttributes(object, node, object.getClass());
105 			return node;
106 		} else {
107 			throw new IllegalArgumentException("Object " + object //$NON-NLS-1$
108 			        + " not mapped."); //$NON-NLS-1$
109 		}
110 	}
111 
112 	/**
113 	 * Save links and objects linked to given object.
114 	 *
115 	 * @param object
116 	 *            The object whose links are saved
117 	 * @param node
118 	 *            The metamodel node of the saved object
119 	 * @throws Exception
120 	 *
121 	 */
122 	private void saveLinks(Mappable object, Node node, Class<Mappable> klass) throws Exception {
123 		if (Helper.isSuperMapped(klass)) {
124 			saveLinks(object, node, (Class<Mappable>)klass.getSuperclass());
125 			// TODO DO NOT USE PRINTLN
126 			// System.out.println("Saving... " + klass.getSimpleName() +" " +
127 			// object);
128 		}
129 
130 		// TODO custom save !!
131 		for (LinkEntry entry : Helper.getMappedLinks(klass)) {
132 			Object linkedItem = entry.getMethod().invoke(object);
133 			int position = 0;
134 			if (linkedItem instanceof List) {
135 				List<Mappable> items = (List<Mappable>)linkedItem;
136 				for (Mappable item : items) {
137 					createLink(entry.getAnnotation(), object, item, node, position);
138 					position++;
139 				}
140 			} else {
141 				createLink(entry.getAnnotation(), object, (Mappable)linkedItem, node, position);
142 			}
143 		}
144 	}
145 
146 	/**
147 	 * Create metamodel link between metamodel node representing given item and another node given as argument
148 	 * (representing object which is linked with the item)
149 	 *
150 	 * @param linkMapping
151 	 *            Metamodel data concerning the created link
152 	 * @param thatItem
153 	 *            The item whos node is linked
154 	 * @param thisNode
155 	 *            The other node for which the node is created
156 	 * @throws SQLException
157 	 * @throws Exception
158 	 */
159 	private void createLink(MapLink linkMapping, Mappable thisItem, Mappable thatItem, Node thisNode, int dstPosition)
160 	        throws Exception {
161 		if (!visited.contains(thatItem)) {
162 			queue.addLast(thatItem);
163 			visited.add(thatItem);
164 		}
165 
166 		if (stored.contains(thatItem)) {
167 			// the link is already created
168 			return;
169 		}
170 
171 		// System.out.println("creatLink "+thatItem);
172 		Node thatNode = internalSave(thatItem);
173 		String linkName = linkMapping.name();
174 
175 		// pick the position of self on the other side of the link
176 		Method method = Helper.getAnnotatedMethod(thatItem.getClass(), linkName);
177 		int srcPosition = 0;
178 		Type klass2 = method.getReturnType();
179 		if (klass2 != null && klass2.equals(List.class)) {
180 			Method setter = thatItem.getClass().getMethod(method.getName());
181 			List elements = (List)setter.invoke(thatItem, new Object[] {});
182 			for (Object element : elements) {
183 				if (element.equals(thisItem)) {
184 					break;
185 				}
186 				srcPosition++;
187 			}
188 		}
189 
190 		// create new link between metamodel nodes
191 		Link link;
192 		if (linkMapping.direction() == Direction.DST) {
193 			// thisNode is the dst node
194 			// thatNode is the src node
195 			link = new Link(thatNode, thisNode, linkName, Priority.NONE, Helper.objectAccessor(thatItem.getClass(),
196 			        linkName), Helper.objectAccessor(thisItem.getClass(), linkName), dstPosition, srcPosition);
197 		} else {
198 			// thisNode is the src node
199 			// thatNode node is the dst node
200 			link = new Link(thisNode, thatNode, linkName, Priority.NONE, Helper.objectAccessor(thisItem.getClass(),
201 			        linkName), Helper.objectAccessor(thatItem.getClass(), linkName), srcPosition, dstPosition);
202 		}
203 		// TODO if the link was saved earlier (in previous save session)
204 		// it won't be saved due to SQLConstrainError. Find better way
205 		// of solving this problem.
206 		link.save();
207 	}
208 
209 	/**
210 	 * Save attributes of given object by converting them to metamodel nodes.
211 	 *
212 	 * @param object
213 	 *            The object whose attributes are saved.
214 	 * @param node
215 	 *            The metamodel node of the saved object
216 	 * @throws Exception
217 	 */
218 	private void saveAttributes(Mappable object, Node node, Class klass) throws Exception {
219 		if (Helper.isSuperMapped(klass)) {
220 			saveAttributes(object, node, klass.getSuperclass());
221 		}
222 		// System.out.println("Saving attributes... " + klass.getSimpleName() +"
223 		// " +
224 		// object);
225 		for (AttributeEntry entry : Helper.getMappedAttributes(klass)) {
226 			Object value = entry.getMethod().invoke(object);
227 			String attributeName = entry.getAnnotation().name();
228 			if (attributeName.equals("")) { //$NON-NLS-1$
229 				attributeName = entry.getMethod().getName();
230 			}
231 			List<Link> links = node.links(attributeName);
232 			if (entry.getAnnotation().save().equals(Object.class)) {
233 				// default implementation
234 				Node attributeNode;
235 				if (links.size() == 0) {
236 					// attribute node doesn't exist - create it!
237 					attributeNode = new Node(entry.getAnnotation().typeName(), value);
238 					Link link = node.addDependent(attributeNode, attributeName);
239 					attributeNode.save();
240 					link.save();
241 				} else {
242 					// update attribute node
243 					if (links.size() != 1) {
244 						throw new RuntimeException("Too many links for attribute " + attributeName //$NON-NLS-1$
245 						        + " for " + klass + "[" + node.getId() //$NON-NLS-1$ //$NON-NLS-2$
246 						        + "]"); //$NON-NLS-1$
247 					} else {
248 						attributeNode = links.get(0).getSrc();
249 						attributeNode.setValue(value);
250 						attributeNode.save();
251 					}
252 				}
253 			} else {
254 				Constructor cons = entry.getAnnotation().save().getConstructor(new Class[] { klass });
255 				AttributeSaver saver = (AttributeSaver)cons.newInstance(new Object[] { object });
256 				saver.saveAttribute(node, links);
257 			}
258 		}
259 	}
260 }