001: package org.garret.perst.continuous;
002:
003: import java.lang.reflect.*;
004: import java.util.*;
005:
006: import org.apache.lucene.document.Document;
007: import org.apache.lucene.document.DateTools;
008:
009: import org.garret.perst.*;
010: import org.garret.perst.impl.ClassDescriptor;
011: import org.garret.perst.impl.LinkImpl;
012:
013: class TableDescriptor extends Persistent implements
014: Iterable<CVersionHistory> {
015: static class IndexDescriptor implements IValue {
016: String fieldName;
017: int fieldType;
018: Index<VersionHistorySegment> index;
019:
020: transient boolean unique;
021: transient boolean caseInsensitive;
022: transient Field field;
023:
024: IterableIterator<VersionHistorySegment> find(CVersion v) {
025: Key key = extractKey(v);
026: return key == null ? new EmptyIterator<VersionHistorySegment>()
027: : index.iterator(key, key,
028: GenericIndex.ASCENT_ORDER);
029: }
030:
031: Key checkKey(Key key) {
032: if (key != null) {
033: if (key.type == ClassDescriptor.tpObject
034: && key.oval instanceof CVersion) {
035: key = new Key(((CVersion) key.oval)
036: .getVersionHistory(), key.inclusion != 0);
037: } else if (caseInsensitive
038: && key.oval instanceof String) {
039: key = new Key(((String) key.oval).toLowerCase(),
040: key.inclusion != 0);
041: }
042: }
043: return key;
044: }
045:
046: void remove(VersionHistorySegment vhs, CVersion v) {
047: index.remove(extractKey(v), vhs);
048: }
049:
050: void add(CVersion v) {
051: Key key = extractKey(v);
052: if (key != null) {
053: index.put(key, new VersionHistorySegment(v));
054: }
055: }
056:
057: Key extractKey(CVersion obj) {
058: try {
059: Field f = field;
060: Key key = null;
061: switch (fieldType) {
062: case ClassDescriptor.tpBoolean:
063: key = new Key(f.getBoolean(obj));
064: break;
065: case ClassDescriptor.tpByte:
066: key = new Key(f.getByte(obj));
067: break;
068: case ClassDescriptor.tpShort:
069: key = new Key(f.getShort(obj));
070: break;
071: case ClassDescriptor.tpChar:
072: key = new Key(f.getChar(obj));
073: break;
074: case ClassDescriptor.tpInt:
075: key = new Key(f.getInt(obj));
076: break;
077: case ClassDescriptor.tpObject: {
078: IPersistent ptr = (IPersistent) f.get(obj);
079: if (ptr != null) {
080: if (!ptr.isPersistent()) {
081: index.getStorage().makePersistent(ptr);
082: }
083: key = new Key(ptr);
084: }
085: break;
086: }
087: case ClassDescriptor.tpLong:
088: key = new Key(f.getLong(obj));
089: break;
090: case ClassDescriptor.tpDate: {
091: Date date = (Date) f.get(obj);
092: if (date != null) {
093: key = new Key(date);
094: }
095: break;
096: }
097: case ClassDescriptor.tpFloat:
098: key = new Key(f.getFloat(obj));
099: break;
100: case ClassDescriptor.tpDouble:
101: key = new Key(f.getDouble(obj));
102: break;
103: case ClassDescriptor.tpEnum: {
104: Enum e = (Enum) f.get(obj);
105: if (e != null) {
106: key = new Key(e);
107: }
108: break;
109: }
110: case ClassDescriptor.tpString: {
111: String s = (String) f.get(obj);
112: if (s != null) {
113: if (caseInsensitive) {
114: s = s.toLowerCase();
115: }
116: key = new Key(s.toCharArray());
117: }
118: break;
119: }
120: default:
121: Assert.failed("Invalid type");
122: }
123: return key;
124: } catch (Exception x) {
125: throw new StorageError(StorageError.ACCESS_VIOLATION, x);
126: }
127: }
128: }
129:
130: static class FullTextSearchableFieldDescriptor {
131: Field field;
132: FullTextSearchableFieldDescriptor[] components;
133:
134: FullTextSearchableFieldDescriptor(Field f) {
135: field = f;
136: }
137: }
138:
139: boolean isFullTextSearchable() {
140: return fullTextSearchableFields.length != 0;
141: }
142:
143: String className;
144: IPersistentSet<CVersionHistory> classExtent;
145: IndexDescriptor[] indices;
146:
147: transient FullTextSearchableFieldDescriptor[] fullTextSearchableFields;
148: transient Field[] cloneableFields;
149: transient Class type;
150: transient boolean notVersioned;
151:
152: public Iterator<CVersionHistory> iterator() {
153: return classExtent.iterator();
154: }
155:
156: static Field[] buildIndexableList(Class type) {
157: ArrayList<Field> fieldList = new ArrayList<Field>();
158: do {
159: for (Field f : type.getDeclaredFields()) {
160: if ((f.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0
161: && f.getAnnotation(Indexable.class) != null) {
162: try {
163: f.setAccessible(true);
164: } catch (Exception x) {
165: }
166:
167: fieldList.add(f);
168: }
169: }
170: } while ((type = type.getSuperclass()) != null);
171:
172: Field[] arr = fieldList.toArray(new Field[fieldList.size()]);
173: Arrays.sort(arr, new Comparator<Field>() {
174: public int compare(Field f1, Field f2) {
175: return f1.getName().compareTo(f2.getName());
176: }
177: });
178: return arr;
179: }
180:
181: static FullTextSearchableFieldDescriptor[] buildFullTextSearchableList(
182: Class type) {
183: ArrayList<FullTextSearchableFieldDescriptor> fieldList = new ArrayList<FullTextSearchableFieldDescriptor>();
184: do {
185: for (Field f : type.getDeclaredFields()) {
186: if ((f.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0
187: && f.getAnnotation(FullTextSearchable.class) != null) {
188: try {
189: f.setAccessible(true);
190: } catch (Exception x) {
191: }
192:
193: FullTextSearchableFieldDescriptor desc = new FullTextSearchableFieldDescriptor(
194: f);
195: Class fieldType = f.getType();
196: if (fieldType != String.class) {
197: FullTextSearchableFieldDescriptor[] searchableComponents = buildFullTextSearchableList(fieldType);
198: if (searchableComponents.length != 0) {
199: desc.components = searchableComponents;
200: }
201: }
202: fieldList.add(desc);
203: }
204: }
205: } while ((type = type.getSuperclass()) != null);
206:
207: return fieldList
208: .toArray(new FullTextSearchableFieldDescriptor[fieldList
209: .size()]);
210: }
211:
212: static Field[] buildCloneableList(Class type) {
213: ArrayList<Field> fieldList = new ArrayList<Field>();
214: do {
215: for (Field f : type.getDeclaredFields()) {
216: if ((f.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) {
217: Class fieldType = f.getType();
218: if (fieldType == Link.class
219: || ICloneable.class
220: .isAssignableFrom(fieldType)
221: || fieldType.isArray()) {
222: try {
223: f.setAccessible(true);
224: } catch (Exception x) {
225: }
226:
227: fieldList.add(f);
228: }
229: }
230: }
231: } while ((type = type.getSuperclass()) != null);
232:
233: return fieldList.toArray(new Field[fieldList.size()]);
234: }
235:
236: public void onLoad() {
237: Storage storage = getStorage();
238: type = ClassDescriptor.loadClass(storage, className);
239: notVersioned = type.getAnnotation(NotVersioned.class) != null;
240: boolean modified = false;
241:
242: Field[] indexableFields = buildIndexableList(type);
243: IndexDescriptor[] newIndices = new IndexDescriptor[indexableFields.length];
244: ArrayList<IndexDescriptor> addedIndices = new ArrayList<IndexDescriptor>();
245:
246: for (int i = 0, j = 0; i < indexableFields.length; i++) {
247: String name = indexableFields[i].getName();
248: while (j < indices.length
249: && indices[j].fieldName.compareTo(name) < 0) {
250: indices[j].index.deallocate();
251: modified = true;
252: j += 1;
253: }
254: IndexDescriptor index;
255: Field f = indexableFields[i];
256: if (j < indices.length && indices[j].fieldName.equals(name)) {
257: index = indices[j++];
258: } else {
259: index = new IndexDescriptor();
260: index.index = storage
261: .<VersionHistorySegment> createIndex(f
262: .getType(), false);
263: index.fieldName = name;
264: index.fieldType = ClassDescriptor.getTypeCode(f
265: .getType());
266: addedIndices.add(index);
267: modified = true;
268: }
269: index.field = f;
270: Indexable idx = (Indexable) f
271: .getAnnotation(Indexable.class);
272: index.unique = idx.unique();
273: index.caseInsensitive = idx.caseInsensitive();
274: newIndices[i] = index;
275: }
276: fullTextSearchableFields = buildFullTextSearchableList(type);
277: cloneableFields = buildCloneableList(type);
278: indices = newIndices;
279: if (modified) {
280: store();
281: if (addedIndices.size() != 0) {
282: addIndices(addedIndices);
283: }
284: }
285: }
286:
287: void addIndices(List<IndexDescriptor> addedIndices) {
288: for (CVersionHistory vh : classExtent) {
289: int last = vh.getNumberOfVersions();
290: for (IndexDescriptor desc : addedIndices) {
291: int from = 0;
292: CVersion v;
293: Key key = null;
294: while (true) {
295: while (++from <= last
296: && ((v = vh.get(from)).isDeleted() || (key = desc
297: .extractKey(v)) == null))
298: ;
299: if (from <= last) {
300: int till = from;
301: while (++till <= last
302: && !(v = vh.get(till)).isDeleted()
303: && key.equals(desc.extractKey(v)))
304: ;
305: desc.index.put(key, new VersionHistorySegment(
306: vh, from, till - 1));
307: from = till - 1;
308: } else {
309: break;
310: }
311: }
312: }
313: }
314: }
315:
316: void cloneFields(CVersion v) {
317: try {
318: for (Field f : cloneableFields) {
319: Object o = f.get(v);
320: if (o != null) {
321: if (o instanceof LinkImpl) {
322: o = new LinkImpl((Link) o, v);
323: } else if (o instanceof ICloneable[]) {
324: ICloneable[] arr = (ICloneable[]) ((Object[]) o)
325: .clone();
326: for (int i = 0; i < arr.length; i++) {
327: arr[i] = (ICloneable) arr[i].clone();
328: }
329: } else if (o instanceof Object[]) {
330: o = ((Object[]) o).clone();
331: } else {
332: o = ((ICloneable) o).clone();
333: }
334: f.set(v, o);
335: }
336: }
337: } catch (CloneNotSupportedException x) {
338: throw new CloneNotSupportedError();
339: } catch (IllegalAccessException x) {
340: throw new IllegalAccessError();
341: }
342: }
343:
344: void excludeFromIndices(CVersion v) {
345: for (IndexDescriptor desc : indices) {
346: for (VersionHistorySegment vhs : desc.find(v)) {
347: if (vhs.vh == v.history && vhs.from == v.id) {
348: if (vhs.from + 1 == vhs.till) {
349: desc.remove(vhs, v);
350: } else {
351: vhs.decrement();
352: }
353: break;
354: }
355: }
356: }
357: }
358:
359: void includeInIndices(CVersion v) {
360: ForEachIndex: for (IndexDescriptor desc : indices) {
361: for (VersionHistorySegment vhs : desc.find(v)) {
362: if (vhs.vh == v.history && vhs.till + 1 == v.id) {
363: vhs.increment();
364: continue ForEachIndex;
365: }
366: }
367: desc.add(v);
368: }
369: }
370:
371: void checkConstraints(CVersion v) throws NotUniqueException {
372: for (IndexDescriptor desc : indices) {
373: if (desc.unique) {
374: for (VersionHistorySegment vhs : desc.find(v)) {
375: if (vhs.vh != v.history
376: && vhs.containsLastVersion()) {
377: throw new NotUniqueException(v);
378: }
379: }
380: }
381: }
382: }
383:
384: IndexDescriptor findIndex(String name) {
385: for (int i = 0; i < indices.length; i++) {
386: if (name.equals(indices[i].fieldName)) {
387: return indices[i];
388: }
389: }
390: return null;
391: }
392:
393: void registerIndices(Query q, IResource resource,
394: VersionSelector selector) {
395: for (IndexDescriptor desc : indices) {
396: q.addIndex(desc.fieldName, new IndexFilter(desc.index,
397: resource, selector));
398: }
399: }
400:
401: void addDocumentFields(
402: Document doc,
403: Object obj,
404: FullTextSearchableFieldDescriptor[] fullTextSearchableFields,
405: StringBuilder any) {
406: try {
407: for (FullTextSearchableFieldDescriptor desc : fullTextSearchableFields) {
408: Object value = desc.field.get(obj);
409: if (value != null) {
410: if (desc.components != null) {
411: addDocumentFields(doc, value, desc.components,
412: any);
413: } else {
414: String s = value.toString();
415: any.append(s);
416: any.append(' ');
417: doc
418: .add(new org.apache.lucene.document.Field(
419: desc.field.getName(),
420: s,
421: org.apache.lucene.document.Field.Store.YES,
422: org.apache.lucene.document.Field.Index.TOKENIZED));
423: }
424: }
425: }
426: } catch (IllegalAccessException x) {
427: throw new IllegalAccessError();
428: }
429: }
430:
431: Document buildDocument(CVersion v) {
432: if (fullTextSearchableFields.length == 0) {
433: return null;
434: }
435: Document doc = new Document();
436: StringBuilder any = new StringBuilder();
437: addDocumentFields(doc, v, fullTextSearchableFields, any);
438: doc.add(new org.apache.lucene.document.Field("Oid", Integer
439: .toString(v.getOid()),
440: org.apache.lucene.document.Field.Store.YES,
441: org.apache.lucene.document.Field.Index.UN_TOKENIZED));
442: doc.add(new org.apache.lucene.document.Field("Class", v
443: .getClass().getName(),
444: org.apache.lucene.document.Field.Store.YES,
445: org.apache.lucene.document.Field.Index.UN_TOKENIZED));
446: doc.add(new org.apache.lucene.document.Field("Created",
447: DateTools.timeToString(System.currentTimeMillis(),
448: DateTools.Resolution.MINUTE),
449: org.apache.lucene.document.Field.Store.YES,
450: org.apache.lucene.document.Field.Index.UN_TOKENIZED));
451: doc.add(new org.apache.lucene.document.Field("Any", any
452: .toString(),
453: org.apache.lucene.document.Field.Store.YES,
454: org.apache.lucene.document.Field.Index.TOKENIZED));
455: return doc;
456: }
457:
458: TableDescriptor(Storage storage, Class table) {
459: super (storage);
460: type = table;
461: notVersioned = type.getAnnotation(NotVersioned.class) != null;
462: className = table.getName();
463: classExtent = storage.<CVersionHistory> createSet();
464:
465: fullTextSearchableFields = buildFullTextSearchableList(table);
466: Field[] indexableFields = buildIndexableList(table);
467: indices = new IndexDescriptor[indexableFields.length];
468: for (int i = 0; i < indexableFields.length; i++) {
469: Field f = indexableFields[i];
470: String name = f.getName();
471: //System.out.println("Create index for field " + table.getName () + '.' + name);
472: IndexDescriptor index = new IndexDescriptor();
473: index.index = storage.<VersionHistorySegment> createIndex(f
474: .getType(), false);
475: index.field = f;
476: index.fieldName = name;
477: index.fieldType = ClassDescriptor.getTypeCode(f.getType());
478: Indexable idx = (Indexable) f
479: .getAnnotation(Indexable.class);
480: index.unique = idx.unique();
481: index.caseInsensitive = idx.caseInsensitive();
482: indices[i] = index;
483: }
484: }
485: }
|