View Javadoc
1   package de.fhdw.wtf.context.model.collections;
2   
3   import java.math.BigInteger;
4   import java.util.HashMap;
5   import java.util.Iterator;
6   
7   import de.fhdw.wtf.context.core.ObjectFactoryProvider;
8   import de.fhdw.wtf.context.core.TransactionManager;
9   import de.fhdw.wtf.context.exception.FrameworkException;
10  import de.fhdw.wtf.context.model.AnyType;
11  import de.fhdw.wtf.context.model.Anything;
12  import de.fhdw.wtf.context.model.Int;
13  import de.fhdw.wtf.context.model.Str;
14  import de.fhdw.wtf.persistence.meta.MapLink;
15  import de.fhdw.wtf.persistence.meta.Object;
16  import de.fhdw.wtf.persistence.meta.UserObject;
17  import de.fhdw.wtf.persistence.utils.Tuple;
18  
19  /**
20   * This class represents Maps. It contains 3 valued associations with one value K as key and another value V as value.
21   * Values can be accessed via the key.
22   * 
23   * @param <K>
24   *            Type of key elements.
25   * @param <V>
26   *            Type of value elements.
27   */
28  public class PersistentMapWithKeyValueLinks<K extends Anything, V extends Anything> implements Map<K, V> {
29  	
30  	/**
31  	 * Maps keys to MapLinks and the values behind the links.
32  	 */
33  	private final HashMap<K, Tuple<MapLink, V>> mapCache;
34  	
35  	/**
36  	 * The name of the association to which this collection belongs.
37  	 */
38  	private final String associationName;
39  	
40  	/**
41  	 * The owner object of this association.
42  	 */
43  	private final UserObject owner;
44  	
45  	/**
46  	 * Creates a PersistentMapWithKeyValueLinks.
47  	 * 
48  	 * @param owner
49  	 *            The owner of the association.
50  	 * @param associationName
51  	 *            The name of the association.
52  	 */
53  	public PersistentMapWithKeyValueLinks(final UserObject owner, final String associationName) {
54  		this.owner = owner;
55  		this.associationName = associationName;
56  		this.mapCache = new HashMap<>();
57  	}
58  	
59  	@Override
60  	public void put(final K key, final V value) {
61  		if (key == null || value == null) {
62  			throw new NullPointerException();
63  		}
64  		// set value in DB
65  		// TODO visitor für anything
66  		MapLink returnedLink;
67  		if (key instanceof Str) {
68  			final String castedKeyString = ((Str) key).toString();
69  			if (value instanceof Str) {
70  				final Str castedValue = (Str) value;
71  				returnedLink =
72  						TransactionManager.getContext().put(
73  								this.owner,
74  								this.associationName,
75  								castedValue.toString(),
76  								castedKeyString);
77  			} else if (value instanceof Int) {
78  				final Int castedValue = (Int) value;
79  				returnedLink =
80  						TransactionManager.getContext().put(
81  								this.owner,
82  								this.associationName,
83  								castedValue.getVal(),
84  								castedKeyString);
85  			} else if (value instanceof AnyType) {
86  				final AnyType castedValue = (AnyType) value;
87  				returnedLink =
88  						TransactionManager.getContext().put(
89  								this.owner,
90  								this.associationName,
91  								castedValue.getObject(),
92  								castedKeyString);
93  			} else {
94  				throw new FrameworkException("Type to insert is not known");
95  			}
96  		} else if (key instanceof Int) {
97  			final BigInteger castedKeyObject = ((Int) key).getVal();
98  			if (value instanceof Str) {
99  				final Str castedValue = (Str) value;
100 				returnedLink =
101 						TransactionManager.getContext().put(
102 								this.owner,
103 								this.associationName,
104 								castedValue.toString(),
105 								castedKeyObject);
106 			} else if (value instanceof Int) {
107 				final Int castedValue = (Int) value;
108 				returnedLink =
109 						TransactionManager.getContext().put(
110 								this.owner,
111 								this.associationName,
112 								castedValue.getVal(),
113 								castedKeyObject);
114 			} else if (value instanceof AnyType) {
115 				final AnyType castedValue = (AnyType) value;
116 				returnedLink =
117 						TransactionManager.getContext().put(
118 								this.owner,
119 								this.associationName,
120 								castedValue.getObject(),
121 								castedKeyObject);
122 			} else {
123 				throw new FrameworkException("Type to insert is not known");
124 			}
125 		} else if (key instanceof AnyType) {
126 			final UserObject castedKeyObject = ((AnyType) key).getObject();
127 			if (value instanceof Str) {
128 				final Str castedValue = (Str) value;
129 				returnedLink =
130 						TransactionManager.getContext().put(
131 								this.owner,
132 								this.associationName,
133 								castedValue.toString(),
134 								castedKeyObject);
135 			} else if (value instanceof Int) {
136 				final Int castedValue = (Int) value;
137 				returnedLink =
138 						TransactionManager.getContext().put(
139 								this.owner,
140 								this.associationName,
141 								castedValue.getVal(),
142 								castedKeyObject);
143 			} else if (value instanceof AnyType) {
144 				final AnyType castedValue = (AnyType) value;
145 				returnedLink =
146 						TransactionManager.getContext().put(
147 								this.owner,
148 								this.associationName,
149 								castedValue.getObject(),
150 								castedKeyObject);
151 			} else {
152 				throw new FrameworkException("Type to insert is not known");
153 			}
154 		} else {
155 			throw new FrameworkException("Type to insert is not known");
156 		}
157 		// set value in internal map
158 		this.mapCache.put(key, new Tuple<>(returnedLink, value));
159 	}
160 	
161 	@Override
162 	public V get(final K key) {
163 		if (key == null) {
164 			throw new NullPointerException();
165 		}
166 		// if key is in internal map => get from internal map
167 		// otherwise get from db
168 		if (this.mapCache.containsKey(key)) {
169 			return this.getFromCache(key);
170 		} else {
171 			java.util.Collection<Tuple<MapLink, Object>> databaseResult = null;
172 			if (key instanceof Str) {
173 				databaseResult =
174 						TransactionManager.getContext().get(this.owner, this.associationName, ((Str) key).toString());
175 			} else if (key instanceof Int) {
176 				databaseResult =
177 						TransactionManager.getContext().get(this.owner, this.associationName, ((Int) key).getVal());
178 			} else if (key instanceof AnyType) {
179 				databaseResult =
180 						TransactionManager.getContext().get(
181 								this.owner,
182 								this.associationName,
183 								((AnyType) key).getObject());
184 			} else {
185 				throw new FrameworkException("Type to insert is not known");
186 			}
187 			final Iterator<Tuple<MapLink, Object>> iterator = databaseResult.iterator();
188 			// No results
189 			if (!iterator.hasNext()) {
190 				this.mapCache.put(key, null);
191 				return null;
192 			}
193 			// at least one result
194 			final Tuple<MapLink, Object> firstResult = iterator.next();
195 			final Object firstResultTarget = firstResult.getSecond();
196 			@SuppressWarnings("unchecked")
197 			final V result = (V) ObjectFactoryProvider.instance().createObject(firstResultTarget);
198 			this.mapCache.put(key, new Tuple<>(firstResult.getFirst(), result));
199 			return this.getFromCache(key);
200 		}
201 	}
202 	
203 	/**
204 	 * Tries to look up a key in the MapLinks map.
205 	 * 
206 	 * @param key
207 	 *            The key.
208 	 * @return The value or null if not found.
209 	 */
210 	private V getFromCache(final K key) {
211 		final Tuple<MapLink, V> localTupel = this.mapCache.get(key);
212 		if (localTupel == null) {
213 			return null;
214 		}
215 		return localTupel.getSecond();
216 	}
217 	
218 	@Override
219 	public V remove(final K key) {
220 		if (key == null) {
221 			throw new NullPointerException();
222 		}
223 		final Tuple<MapLink, V> toRemoveTuple = this.mapCache.get(key);
224 		// remove in DB
225 		TransactionManager.getContext().unset(toRemoveTuple.getFirst());
226 		// remove local, but keep in mind that the key was received from DB
227 		this.mapCache.put(key, null);
228 		return toRemoveTuple.getSecond();
229 	}
230 	
231 	// @Override
232 	// public void clear() {
233 	// //clear local map
234 	// //clear DB map
235 	// //database operation needed (not possible to do client side, because all
236 	// keys would be retrieved)
237 	// }
238 	
239 }