001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.internal.queryframework;
038:
039: import java.security.AccessController;
040: import java.security.PrivilegedActionException;
041: import java.util.*;
042: import java.lang.reflect.*;
043:
044: import oracle.toplink.essentials.exceptions.*;
045: import oracle.toplink.essentials.internal.ejb.cmp3.base.CMP3Policy;
046: import oracle.toplink.essentials.internal.helper.*;
047: import oracle.toplink.essentials.queryframework.DatabaseQuery;
048: import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
049: import oracle.toplink.essentials.internal.security.PrivilegedClassForName;
050: import oracle.toplink.essentials.internal.security.PrivilegedMethodInvoker;
051: import oracle.toplink.essentials.internal.security.PrivilegedGetValueFromField;
052: import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
053: import oracle.toplink.essentials.internal.sessions.AbstractSession;
054:
055: /**
056: * <p><b>Purpose</b>: A MapContainerPolicy is ContainerPolicy whose container
057: * class implements the Map interface.
058: * <p>
059: * <p><b>Responsibilities</b>:
060: * Provide the functionality to operate on an instance of a Map.
061: *
062: * @see ContainerPolicy
063: * @see CollectionContainerPolicy
064: */
065: public class MapContainerPolicy extends InterfaceContainerPolicy {
066: protected String keyName;
067: protected String elementClassName;
068: protected Class elementClass;
069: protected transient Field keyField;
070: protected transient Method keyMethod;
071:
072: /**
073: * INTERNAL:
074: * Construct a new policy.
075: */
076: public MapContainerPolicy() {
077: super ();
078: }
079:
080: /**
081: * INTERNAL:
082: * Construct a new policy for the specified class.
083: */
084: public MapContainerPolicy(Class containerClass) {
085: super (containerClass);
086: }
087:
088: /**
089: * INTERNAL:
090: * Construct a new policy for the specified class name.
091: */
092: public MapContainerPolicy(String containerClassName) {
093: super (containerClassName);
094: }
095:
096: /**
097: * Prepare and validate.
098: * Set the element class.
099: */
100: public void prepare(DatabaseQuery query, AbstractSession session)
101: throws QueryException {
102: if ((getElementClass() == null)
103: && (query.getDescriptor() != null)) {
104: setElementClass(query.getDescriptor().getJavaClass());
105: }
106:
107: super .prepare(query, session);
108: }
109:
110: /**
111: * INTERNAL:
112: * Add element into container which implements the Map interface.
113: */
114: public boolean addInto(Object key, Object element,
115: Object container, AbstractSession session) {
116: Object wrapped = element;
117:
118: if (hasElementDescriptor()) {
119: wrapped = getElementDescriptor().getObjectBuilder()
120: .wrapObject(element, session);
121: }
122:
123: try {
124: if (key != null) {
125: return ((Map) container).put(key, wrapped) != null;
126: } else {
127: return ((Map) container).put(keyFrom(element, session),
128: wrapped) != null;
129: }
130: } catch (ClassCastException ex1) {
131: throw QueryException
132: .mapKeyNotComparable(element, container);
133: }
134: }
135:
136: /**
137: * INTERNAL:
138: * Remove all the elements from container.
139: */
140: public void clear(Object container) {
141: try {
142: ((Map) container).clear();
143: } catch (UnsupportedOperationException ex) {
144: throw QueryException.methodNotValid(container, "clear()");
145: }
146: }
147:
148: /**
149: * INTERNAL:
150: * Return true if keys are the same in the source as the backup. False otherwise
151: * in the case of readonly compare against the original
152: */
153: public boolean compareKeys(Object sourceValue,
154: AbstractSession session) {
155: Object backUpVersion = null;
156:
157: if (((UnitOfWorkImpl) session).isClassReadOnly(sourceValue
158: .getClass())) {
159: backUpVersion = ((UnitOfWorkImpl) session)
160: .getOriginalVersionOfObject(sourceValue);
161: } else {
162: backUpVersion = ((UnitOfWorkImpl) session)
163: .getBackupClone(sourceValue);
164: }
165:
166: return (keyFrom(backUpVersion, session).equals(keyFrom(
167: sourceValue, session)));
168: }
169:
170: /**
171: * INTERNAL:
172: * Return the true if element exists in container.
173: * @return boolean true if container 'contains' element
174: */
175: protected boolean contains(Object element, Object container) {
176: return ((Map) container).containsValue(element);
177: }
178:
179: /**
180: * INTERNAL:
181: * Convert all the class-name-based settings in this ContainerPolicy to
182: * actual class-based settings. This method is used when converting a
183: * project that has been built with class names to a project with classes.
184: * @param classLoader
185: */
186: public void convertClassNamesToClasses(ClassLoader classLoader) {
187: super .convertClassNamesToClasses(classLoader);
188:
189: if (elementClassName == null) {
190: return;
191: }
192:
193: try {
194: Class elementClass = null;
195: if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
196: try {
197: elementClass = (Class) AccessController
198: .doPrivileged(new PrivilegedClassForName(
199: elementClassName, true, classLoader));
200: } catch (PrivilegedActionException exception) {
201: throw ValidationException
202: .classNotFoundWhileConvertingClassNames(
203: containerClassName, exception
204: .getException());
205: }
206: } else {
207: elementClass = oracle.toplink.essentials.internal.security.PrivilegedAccessHelper
208: .getClassForName(elementClassName, true,
209: classLoader);
210: }
211: setElementClass(elementClass);
212: } catch (ClassNotFoundException exc) {
213: throw ValidationException
214: .classNotFoundWhileConvertingClassNames(
215: containerClassName, exc);
216: }
217: }
218:
219: /**
220: * INTERNAL:
221: * Returns the element class which defines the map key.
222: */
223: public Class getElementClass() {
224: return elementClass;
225: }
226:
227: /**
228: * INTERNAL:
229: * Returns the element class name which defines the map key.
230: */
231: public String getElementClassName() {
232: return elementClassName;
233: }
234:
235: /**
236: * INTERNAL:
237: */
238: public Class getInterfaceType() {
239: return ClassConstants.Map_Class;
240: }
241:
242: /**
243: * INTERNAL:
244: * Returns the key name which will return the value of the key to be used
245: * in the container.
246: */
247: public String getKeyName() {
248: return keyName;
249: }
250:
251: /**
252: * INTERNAL
253: * Yes this is a MapPolicy
254: */
255: public boolean isMapPolicy() {
256: return true;
257: }
258:
259: /**
260: * INTERNAL:
261: * Return an Iterator for the given container.
262: */
263: public Object iteratorFor(Object container) {
264: return ((Map) container).values().iterator();
265: }
266:
267: /**
268: * INTERNAL:
269: * Return the key for the specified element.
270: */
271: public Object keyFrom(Object element, AbstractSession session) {
272: // Should only run through this once ...
273: if (keyName != null && keyMethod == null && keyField == null) {
274: try {
275: keyMethod = Helper.getDeclaredMethod(elementClass,
276: keyName, (Class[]) null);
277: } catch (NoSuchMethodException ex) {
278: try {
279: keyField = Helper.getField(elementClass, keyName);
280: } catch (NoSuchFieldException e) {
281: throw ValidationException
282: .mapKeyNotDeclaredInItemClass(keyName,
283: elementClass);
284: }
285: }
286: }
287:
288: Object keyElement = element;
289:
290: if (hasElementDescriptor()) {
291: keyElement = getElementDescriptor().getObjectBuilder()
292: .unwrapObject(element, session);
293: }
294:
295: if (keyMethod != null) {
296: try {
297: if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
298: try {
299: return AccessController
300: .doPrivileged(new PrivilegedMethodInvoker(
301: keyMethod, keyElement,
302: (Object[]) null));
303: } catch (PrivilegedActionException exception) {
304: Exception throwableException = exception
305: .getException();
306: if (throwableException instanceof IllegalAccessException) {
307: throw QueryException
308: .cannotAccessMethodOnObject(
309: keyMethod, keyElement);
310: } else {
311: throw QueryException
312: .calledMethodThrewException(
313: keyMethod, keyElement,
314: throwableException);
315: }
316: }
317: } else {
318: return PrivilegedAccessHelper.invokeMethod(
319: keyMethod, keyElement, (Object[]) null);
320: }
321: } catch (IllegalAccessException e) {
322: throw QueryException.cannotAccessMethodOnObject(
323: keyMethod, keyElement);
324: } catch (InvocationTargetException exception) {
325: throw QueryException.calledMethodThrewException(
326: keyMethod, keyElement, exception);
327: }
328: } else if (keyField != null) {
329: try {
330: if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
331: try {
332: return AccessController
333: .doPrivileged(new PrivilegedGetValueFromField(
334: keyField, keyElement));
335: } catch (PrivilegedActionException exception) {
336: throw QueryException.cannotAccessFieldOnObject(
337: keyField, keyElement);
338: }
339: } else {
340: return oracle.toplink.essentials.internal.security.PrivilegedAccessHelper
341: .getValueFromField(keyField, keyElement);
342: }
343: } catch (IllegalAccessException e) {
344: throw QueryException.cannotAccessFieldOnObject(
345: keyField, keyElement);
346: }
347: } else {
348: // If we get this far I think it is safe to assume we have
349: // an element descriptor.
350: return ((CMP3Policy) getElementDescriptor().getCMPPolicy())
351: .createPrimaryKeyInstance(keyElement, session);
352: }
353: }
354:
355: /**
356: * INTERNAL:
357: * Remove element from container which implements the Map interface.
358: */
359: public boolean removeFrom(Object key, Object element,
360: Object container, AbstractSession session) {
361: try {
362: Object returnValue = null;
363: if (key != null) {
364: returnValue = ((Map) container).remove(key);
365: } else {
366: returnValue = ((Map) container).remove(keyFrom(element,
367: session));
368: }
369: if (returnValue == null) {
370: return false;
371: } else {
372: return true;
373: }
374: } catch (UnsupportedOperationException ex) {
375: throw QueryException.methodNotValid(container,
376: "remove(Object element)");
377: }
378: }
379:
380: /**
381: * INTERNAL:
382: * Remove element from container which implements the Map interface.
383: */
384: public boolean removeFromWithIdentity(Object element,
385: Object container, AbstractSession session) {
386: boolean found = false;
387: Vector knownKeys = new Vector(1);
388: try {
389: Iterator iterator = ((Map) container).keySet().iterator();
390: while (iterator.hasNext()) {
391: Object key = iterator.next();
392: if (((Map) container).get(key) == element) {
393: knownKeys.addElement(key);
394: found = true;
395: }
396: }
397: if (found) {
398: for (int index = 0; index < knownKeys.size(); ++index) {
399: ((Map) container)
400: .remove(knownKeys.elementAt(index));
401: }
402: }
403: return found;
404: } catch (UnsupportedOperationException ex) {
405: throw QueryException.methodNotValid(container,
406: "remove(Object element)");
407: }
408: }
409:
410: /**
411: * INTERNAL:
412: * Sets the element class which defines the method.
413: */
414: public void setElementClass(Class elementClass) {
415: if (elementClass != null) {
416: elementClassName = elementClass.getName();
417: }
418:
419: this .elementClass = elementClass;
420: }
421:
422: /**
423: * INTERNAL:
424: * Validate the container type.
425: */
426: public boolean isValidContainer(Object container) {
427: // PERF: Use instanceof which is inlined, not isAssignable which
428: // is very inefficent.
429: return container instanceof Map;
430: }
431:
432: /**
433: * INTERNAL:
434: * Sets the key name to be used to generate the key in a Map type container
435: * class. The key name, may be the name of a field or method.
436: */
437: public void setKeyName(String keyName, String elementClassName) {
438: // The key name and class name must be held as the policy is used
439: // directly from the mapping.
440: this .keyName = keyName;
441: this .elementClassName = elementClassName;
442: }
443:
444: /**
445: * INTERNAL:
446: * Sets the key name to be used to generate the key in a Map type container
447: * class. The key name, maybe the name of a field or method.
448: */
449: public void setKeyName(String keyName) {
450: this .keyName = keyName;
451: }
452:
453: /**
454: * INTERNAL:
455: * Return the size of container.
456: */
457: public int sizeFor(Object container) {
458: return ((Map) container).size();
459: }
460:
461: /**
462: * INTERNAL:
463: * If the key has changed, remove the element and add it back into the target.
464: */
465: public void validateElementAndRehashIfRequired(Object sourceValue,
466: Object targetMap, AbstractSession session,
467: Object targetVersionOfSource) {
468: if (session.isUnitOfWork()) {
469: //this must be a unit of work at this point
470: Object backupValue = ((UnitOfWorkImpl) session)
471: .getBackupClone(sourceValue);
472: if (!keyFrom(backupValue, session).equals(
473: keyFrom(sourceValue, session))) {
474: //the key has been changed. Remove the old value and put back the new one
475: removeFrom(backupValue, targetMap, session);
476: addInto(targetVersionOfSource, targetMap, session);
477: }
478: }
479: }
480: }
|