001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.metadata.parser;
012:
013: import com.versant.core.common.Debug;
014: import com.versant.core.util.BeanUtils;
015: import com.versant.core.util.IntObjectHashMap;
016:
017: import java.io.PrintStream;
018: import java.lang.reflect.Field;
019: import java.util.*;
020:
021: import com.versant.core.common.BindingSupportImpl;
022:
023: /**
024: * This is one of our vendor extensions with the key string converted to
025: * an int constant for easy processing.
026: */
027: public final class JdoExtension extends JdoElement implements
028: JdoExtensionKeys {
029:
030: /**
031: * This is the special value used to indicate that constraints, indexes
032: * and so on should not be generated.
033: */
034: public static final String NO_VALUE = "{no}";
035:
036: public static final String NAME_VALUE = "{name}";
037: public static final String FULLNAME_VALUE = "{fullname}";
038: public static final String HASH_VALUE = "{hash}";
039:
040: public int key;
041: public String value;
042: public JdoExtension[] nested;
043: public JdoElement parent;
044:
045: private static final Map STR_KEY_MAP = new HashMap();
046: private static final IntObjectHashMap KEY_STR_MAP = new IntObjectHashMap();
047: private static final HashMap PRIMITIVE_TYPE_MAP = new HashMap(17);
048:
049: static {
050: // find all the valid keys and add them to the maps
051: Field[] a = JdoExtensionKeys.class.getFields();
052: for (int i = a.length - 1; i >= 0; i--) {
053: Field f = a[i];
054: String key = f.getName().replace('_', '-').toLowerCase();
055:
056: try {
057: int v = f.getInt(null);
058: STR_KEY_MAP.put(key, new Integer(v));
059: KEY_STR_MAP.put(v, key);
060: } catch (IllegalAccessException e) {
061: throw BindingSupportImpl.getInstance().internal(
062: e.getMessage(), e);
063: }
064: }
065:
066: // build map of primitive types
067: PRIMITIVE_TYPE_MAP.put("boolean", Boolean.TYPE);
068: PRIMITIVE_TYPE_MAP.put("byte", Byte.TYPE);
069: PRIMITIVE_TYPE_MAP.put("char", Character.TYPE);
070: PRIMITIVE_TYPE_MAP.put("short", Short.TYPE);
071: PRIMITIVE_TYPE_MAP.put("int", Integer.TYPE);
072: PRIMITIVE_TYPE_MAP.put("long", Long.TYPE);
073: PRIMITIVE_TYPE_MAP.put("float", Float.TYPE);
074: PRIMITIVE_TYPE_MAP.put("double", Double.TYPE);
075: }
076:
077: public JdoExtension createCopy(JdoElement pe) {
078: JdoExtension copy = new JdoExtension();
079: copy.key = key;
080: copy.value = value;
081:
082: if (nested != null) {
083: copy.nested = new JdoExtension[nested.length];
084: for (int i = 0; i < nested.length; i++) {
085: JdoExtension jdoExtension = nested[i];
086: copy.nested[i] = jdoExtension.createCopy(copy);
087: }
088: }
089: copy.parent = pe;
090: return copy;
091: }
092:
093: public JdoElement getParent() {
094: return parent;
095: }
096:
097: /**
098: * Get information for this element to be used in building up a
099: * context string.
100: *
101: * @see #getContext
102: */
103: public String getSubContext() {
104: if (value == null)
105: return toKeyString(key);
106: return toKeyString(key) + "=\"" + value + '"';
107: }
108:
109: /**
110: * Is this a common extension? This method must be kept in sync with the
111: * constants.
112: *
113: * @see JdoExtensionKeys
114: */
115: public boolean isCommon() {
116: return key < 100;
117: }
118:
119: /**
120: * Is this a jdbc extension? This method must be kept in sync with the
121: * constants.
122: *
123: * @see JdoExtensionKeys
124: */
125: public boolean isJdbc() {
126: return key >= 100 && key <= 199;
127: }
128:
129: /**
130: * Convert a String key value into an int constant.
131: *
132: * @return Key constant or Integer.MIN_VALUE if not valid
133: * @see JdoExtensionKeys
134: */
135: public static int parseKey(String key) {
136: Integer v = (Integer) STR_KEY_MAP.get(key);
137: return v == null ? Integer.MIN_VALUE : v.intValue();
138: }
139:
140: /**
141: * Convert ant int key value into a String.
142: *
143: * @see JdoExtensionKeys
144: */
145: public static String toKeyString(int key) {
146: return (String) KEY_STR_MAP.get(key);
147: }
148:
149: public String toString() {
150: return getSubContext();
151: }
152:
153: public void dump() {
154: dump(Debug.OUT, "");
155: }
156:
157: public void dump(PrintStream out, String indent) {
158: out.println(indent + this );
159: if (nested != null) {
160: for (int i = 0; i < nested.length; i++) {
161: nested[i].dump(out, indent + " ");
162: }
163: }
164: }
165:
166: /**
167: * Get the value of an extension that must be a String.
168: *
169: * @throws javax.jdo.JDOFatalUserException if the value is null
170: */
171: public String getString() {
172: if (value == null) {
173: throw BindingSupportImpl.getInstance().runtime(
174: "Expected 'value' attribute for " + this + "\n"
175: + getContext());
176: }
177: return value;
178: }
179:
180: /**
181: * Is the value of this extension the special NO_VALUE ({no})?
182: */
183: public boolean isNoValue() {
184: return value != null && value.equals(NO_VALUE);
185: }
186:
187: /**
188: * Get the value of an extension that must be a boolean. A null value
189: * is true.
190: *
191: * @throws javax.jdo.JDOFatalUserException if the value is invalid
192: */
193: public boolean getBoolean() {
194: if (value == null || value.equals("true"))
195: return true;
196: if (value.equals("false"))
197: return false;
198: throw BindingSupportImpl.getInstance().runtime(
199: "Expected 'true' or 'false' for " + this + "\n"
200: + getContext());
201: }
202:
203: /**
204: * Get the value of an extension that must be an int.
205: *
206: * @throws javax.jdo.JDOFatalUserException if the value is invalid
207: */
208: public int getInt() {
209: try {
210: if (value != null)
211: return Integer.parseInt(value);
212: } catch (NumberFormatException e) {
213: // ignore
214: }
215: throw BindingSupportImpl.getInstance().runtime(
216: "Expected integer value for " + this + "\n"
217: + getContext());
218: }
219:
220: /**
221: * Get the value of an extension that must be one of a set of enumerated
222: * String's. Each valid String must have an int entry in map. The
223: * corresponding int is returned.
224: *
225: * @throws javax.jdo.JDOFatalUserException if the value is not in map
226: */
227: public int getEnum(Map map) {
228: int ans;
229: if (value == null) {
230: ans = Integer.MIN_VALUE;
231: } else {
232: ans = ((Integer) map.get(value)).intValue();
233: }
234: if (ans != Integer.MIN_VALUE)
235: return ans;
236: // build a nice exception message and throw it
237: ArrayList a = new ArrayList(map.size());
238: for (Iterator i = map.keySet().iterator(); i.hasNext();)
239: a.add(i.next());
240: Collections.sort(a);
241: StringBuffer s = new StringBuffer();
242: if (value == null) {
243: s.append("No value attribute");
244: } else {
245: s.append("Invalid value attribute '");
246: s.append(value);
247: s.append('\'');
248: }
249: s.append(": Expected ");
250: int n = a.size();
251: for (int i = 0; i < n; i++) {
252: if (i > 0) {
253: if (i < n - 1)
254: s.append(", ");
255: else
256: s.append(" or ");
257: }
258: s.append('\'');
259: s.append(a.get(i));
260: s.append('\'');
261: }
262: s.append('\n');
263: s.append(getContext());
264: throw BindingSupportImpl.getInstance().runtime(s.toString());
265: }
266:
267: /**
268: * Get the value of an extension that must be the fully qualified name
269: * of a class or a primitive type. The class is loaded using the supplied
270: * classloader.
271: */
272: public Class getType(ClassLoader loader) {
273: String qname = getString();
274: try {
275: return BeanUtils.loadClass(qname, true, loader);
276: } catch (ClassNotFoundException e) {
277: throw BindingSupportImpl.getInstance()
278: .runtime(
279: "Class " + qname + " not found\n"
280: + getContext(), e);
281: }
282: }
283:
284: /**
285: * Get the value of an extension that must be the fully qualified name
286: * of a class assignable from the supplied class. The class is loaded
287: * using the supplied classloader.
288: */
289: public Class getType(ClassLoader loader, Class requiredType) {
290: Class t = getType(loader);
291: if (requiredType.isAssignableFrom(t))
292: return t;
293: throw BindingSupportImpl.getInstance().runtime(
294: "Class " + t.getName() + " is not a "
295: + requiredType.getName() + "\n" + getContext());
296: }
297:
298: /**
299: * Create a HashMap of properties from our nested property extensions.
300: * If there are none an empty map is returned.
301: */
302: public HashMap getPropertyMap() {
303: return getPropertyMap(new HashMap(17));
304: }
305:
306: /**
307: * Add propertiies to the supplied HashMap.
308: */
309: public HashMap getPropertyMap(HashMap m) {
310: if (nested != null) {
311: int n = nested.length;
312: for (int i = 0; i < n; i++) {
313: JdoExtension e = nested[i];
314: if (e.key != PROPERTY) {
315: throw BindingSupportImpl.getInstance().runtime(
316: "Expected property extension: " + e + "\n"
317: + e.getContext());
318: }
319: String v = nested[i].getString();
320: int pos = v.indexOf('=');
321: if (pos < 0) {
322: throw BindingSupportImpl.getInstance().runtime(
323: "Invalid value attribute, expected 'key=value': '"
324: + v + "'\n" + e.getContext());
325: }
326: String key = v.substring(0, pos);
327: v = v.substring(pos + 1);
328: m.put(key, v);
329: }
330: }
331: return m;
332: }
333:
334: /**
335: * Find an extension from an array of extensions.
336: */
337: public static JdoExtension find(int key, JdoExtension[] a) {
338: if (a == null)
339: return null;
340: int n = a.length;
341: for (int i = 0; i < n; i++)
342: if (a[i].key == key)
343: return a[i];
344: return null;
345: }
346:
347: /**
348: * Create the extension if it does not exist, else update it if overwrite is true
349: * @param key
350: * @param value
351: * @param overwrite
352: */
353: public void findCreate(int key, String value, boolean overwrite) {
354: if (nested == null) {
355: nested = new JdoExtension[] { createChild(key, value) };
356: return;
357: }
358:
359: JdoExtension ext = find(key, nested);
360:
361: if (ext == null) {
362: addExtension(createChild(key, value));
363: } else if (overwrite) {
364: ext.value = value;
365: }
366: }
367:
368: private JdoExtension createChild(int key, String value) {
369: JdoExtension e = new JdoExtension();
370: e.key = key;
371: e.value = value;
372: e.parent = this ;
373: return e;
374: }
375:
376: private void addExtension(JdoExtension e) {
377: JdoExtension[] tmp = new JdoExtension[nested.length + 1];
378: System.arraycopy(nested, 0, tmp, 0, nested.length);
379: tmp[nested.length] = e;
380: nested = tmp;
381: }
382:
383: /**
384: * Find an extension from an array of extensions.
385: */
386: public static JdoExtension find(int key, JdoElement[] a) {
387: if (a == null)
388: return null;
389: int n = a.length;
390: for (int i = 0; i < n; i++) {
391: if (a[i] instanceof JdoExtension) {
392: if (((JdoExtension) a[i]).key == key)
393: return (JdoExtension) a[i];
394: }
395: }
396: return null;
397: }
398:
399: public static JdoExtension find(int key, String value,
400: JdoExtension[] a) {
401: if (a == null)
402: return null;
403: int n = a.length;
404: for (int i = 0; i < n; i++)
405: if (a[i].key == key && a[i].value.equals(value))
406: return a[i];
407: return null;
408: }
409:
410: /**
411: * Does the any of the nested jdoExtension contain the spec key
412: *
413: * @param key The key to search for.
414: * @return
415: */
416: public boolean contains(int key) {
417: if (nested != null) {
418: for (int i = 0; i < nested.length; i++) {
419: JdoExtension jdoExtension = nested[i];
420: if (jdoExtension.key == key)
421: return true;
422: }
423: }
424: return false;
425: }
426:
427: /**
428: * Update the 'to' extension with the 'from' extensions. If there is
429: * elements in 'from' that is not in 'to' then add them. If the element is there
430: * then update it value and recursively synchronize its nested elements.
431: *
432: */
433: public static JdoExtension[] synchronize(JdoExtension[] from,
434: JdoExtension[] to) {
435: if (from == null)
436: return to;
437: if (to == null)
438: return from;
439:
440: ArrayList newExts = new ArrayList(to.length);
441: for (int i = 0; i < from.length; i++) {
442: boolean found = false;
443: JdoExtension fromElement = from[i];
444:
445: for (int j = 0; j < to.length; j++) {
446: JdoExtension toElement = to[j];
447: if (fromElement.key == toElement.key) {
448: switch (fromElement.key) {
449: case JdoExtension.FIELD:
450: if (fromElement.value != null
451: && fromElement.value
452: .equals(toElement.value)) {
453: toElement.nested = JdoExtension
454: .synchronize(fromElement.nested,
455: toElement.nested);
456: }
457: break;
458: case JdoExtension.JDBC_COLUMN:
459: toElement.nested = JdoExtension.synchronize(
460: fromElement.nested, toElement.nested);
461: break;
462: case JdoExtension.JDBC_COLUMN_NAME:
463: toElement.value = fromElement.value;
464: break;
465: }
466: found = true;
467: newExts.add(toElement);
468: break;
469: }
470: }
471:
472: if (!found) {
473: newExts.add(fromElement);
474: }
475: }
476:
477: JdoExtension[] result = new JdoExtension[newExts.size()];
478: newExts.toArray(result);
479: return result;
480: }
481:
482: public static void synchronize3(JdoExtension[] from,
483: JdoExtension[] to, Set exclude, boolean errorOnExclude) {
484: if (from == null)
485: return;
486: if (to == null)
487: return;
488:
489: for (int i = 0; i < from.length; i++) {
490: JdoExtension fromElement = from[i];
491: for (int j = 0; j < to.length; j++) {
492: JdoExtension toElement = to[j];
493: if (fromElement.key == toElement.key) {
494: if (exclude.contains(toKeyString(fromElement.key))) {
495: if (errorOnExclude) {
496: throw BindingSupportImpl.getInstance()
497: .invalidOperation("");
498: }
499: return;
500: }
501: if (fromElement.value != null) {
502: if (!fromElement.value.equals(toElement.value)) {
503: toElement.value = fromElement.value;
504: }
505: }
506: synchronize3(fromElement.nested, toElement.nested,
507: exclude, errorOnExclude);
508: }
509: }
510: }
511: }
512:
513: public static JdoExtension[] synchronize4(JdoExtension[] from,
514: JdoExtension[] to, Set ignore) {
515: if (from == null)
516: return to;
517:
518: List toAdd = new ArrayList();
519: for (int i = 0; i < from.length; i++) {
520: JdoExtension fromElement = from[i];
521: if (!ignore.isEmpty()
522: && ignore.contains(JdoExtension
523: .toKeyString(fromElement.key))) {
524: continue;
525: }
526: boolean found = false;
527: if (to != null) {
528: for (int j = 0; j < to.length; j++) {
529: JdoExtension toElement = to[j];
530: if (fromElement.key == toElement.key) {
531: found = true;
532: if (fromElement.value != null) {
533: if (!fromElement.value
534: .equals(toElement.value)) {
535: toElement.value = fromElement.value;
536: }
537: }
538: toElement.nested = synchronize4(
539: fromElement.nested, toElement.nested,
540: ignore);
541: }
542: }
543: }
544: if (!found) {
545: toAdd.add(fromElement);
546: }
547: }
548: if (!toAdd.isEmpty()) {
549: if (to != null) {
550: for (int i = 0; i < to.length; i++) {
551: toAdd.add(to[i]);
552: }
553: }
554: JdoExtension tmp[] = new JdoExtension[toAdd.size()];
555: toAdd.toArray(tmp);
556: return tmp;
557: }
558: return to;
559: }
560:
561: public boolean isFieldAttribute() {
562: if (key == JdoExtensionKeys.DEFAULT_FETCH_GROUP
563: || key == JdoExtensionKeys.EMBEDDED
564: || key == JdoExtensionKeys.NULL_VALUE) {
565: return true;
566: }
567: return false;
568: }
569:
570: public static void clearKey(int key, int level, JdoExtension[] exts) {
571: if (exts != null) {
572: for (int k = 0; k < exts.length; k++) {
573: JdoExtension ext1 = exts[k];
574: if (ext1.key == key) {
575: ext1.value = null;
576: break;
577: } else if (ext1.nested != null) {
578: for (int l = 0; l < ext1.nested.length; l++) {
579: JdoExtension ext2 = ext1.nested[l];
580: if (ext2.key == key) {
581: ext2.key = -1;
582: } else if (ext2.nested != null) {
583: for (int i = 0; i < ext2.nested.length; i++) {
584: JdoExtension ext3 = ext2.nested[i];
585: if (ext3.key == key) {
586: ext3.key = -1;
587: }
588: }
589: }
590: }
591: }
592: }
593: }
594: }
595: }
|