0001: /*
0002: * Jacareto Copyright (c) 2002-2005
0003: * Applied Computer Science Research Group, Darmstadt University of
0004: * Technology, Institute of Mathematics & Computer Science,
0005: * Ludwigsburg University of Education, and Computer Based
0006: * Learning Research Group, Aachen University. All rights reserved.
0007: *
0008: * Jacareto is free software; you can redistribute it and/or
0009: * modify it under the terms of the GNU General Public
0010: * License as published by the Free Software Foundation; either
0011: * version 2 of the License, or (at your option) any later version.
0012: *
0013: * Jacareto is distributed in the hope that it will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016: * General Public License for more details.
0017: *
0018: * You should have received a copy of the GNU General Public
0019: * License along with Jacareto; if not, write to the Free
0020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
0021: *
0022: */
0023:
0024: package jacareto.struct;
0025:
0026: import jacareto.filter.ClassFilter;
0027: import jacareto.parse.RecordTokenizer;
0028: import jacareto.record.ReadAccessRecord;
0029: import jacareto.record.RecordException;
0030: import jacareto.record.Recordable;
0031: import jacareto.struct.event.StructureElementEvent;
0032: import jacareto.struct.event.StructureElementListener;
0033: import jacareto.system.Environment;
0034: import jacareto.system.EnvironmentMember;
0035: import jacareto.toolkit.UUIDGen;
0036:
0037: import org.apache.commons.lang.Validate;
0038: import org.apache.regexp.RE;
0039:
0040: import java.util.ArrayList;
0041: import java.util.Arrays;
0042: import java.util.Enumeration;
0043: import java.util.Iterator;
0044: import java.util.List;
0045: import java.util.Set;
0046: import java.util.TreeMap;
0047: import java.util.Vector;
0048:
0049: import javax.swing.tree.TreeNode;
0050:
0051: /**
0052: * An element of a record structure. A structure element can parse a part of a record.
0053: *
0054: * @author <a href="mailto:cspannagel@web.de">Christian Spannagel</a>
0055: * @version 1.04
0056: */
0057: public abstract class StructureElement extends EnvironmentMember
0058: implements TreeNode, Cloneable {
0059: /** The parent. */
0060: private StructureElement parent;
0061:
0062: /** The structure this element is contained in. */
0063: private Structure structure;
0064:
0065: /** The array of the children. */
0066: private Vector children;
0067:
0068: /** The duration. */
0069: private long duration;
0070:
0071: /** The processing time. */
0072: private long procTime = 0;
0073:
0074: /** The current procTime measurements */
0075: private List procTimes;
0076:
0077: /** Is this a static structure element */
0078: private boolean isStatic;
0079:
0080: /** Whether or not the element is a search hit. */
0081: private boolean isSearchHit;
0082:
0083: /** Whether or not the element is a parent element of a search hit. */
0084: private boolean isParentOfSearchHit;
0085:
0086: /** The vector of structure element listeners. */
0087: private Vector structureElementListeners;
0088:
0089: /** Whether or not the element notifies listeners of changes. */
0090: private boolean isNotificationEnabled;
0091:
0092: /** The UUID to identify the StructureElement */
0093: private String UUIDString = null;
0094:
0095: /**
0096: * Creates a new structure element with the given parent and the given children.
0097: *
0098: * @param env the environment
0099: * @param parent the parent element
0100: * @param children the children
0101: * @param isStatic is this a user structure element
0102: */
0103: public StructureElement(Environment env, StructureElement parent,
0104: StructureElement[] children, boolean isStatic) {
0105: super (env);
0106: this .isStatic = isStatic;
0107: this .children = new Vector(5, 5);
0108: this .procTimes = new ArrayList();
0109: setIsSearchHit(false);
0110: setIsParentOfSearchHit(false);
0111: setParent(parent);
0112: setChildren(children);
0113: setNotificationEnabled(true);
0114: structureElementListeners = new Vector(5, 5);
0115: calculateDuration();
0116: }
0117:
0118: /**
0119: * Creates a new structure element with the given parent and the given children.
0120: *
0121: * @param env the environment
0122: * @param parent the parent element
0123: * @param children the children
0124: */
0125: public StructureElement(Environment env, StructureElement parent,
0126: StructureElement[] children) {
0127: this (env, parent, children, false);
0128: }
0129:
0130: /**
0131: * Creates a new structure element with no parent and no children and the given environment.
0132: *
0133: * @param env the environment
0134: */
0135: public StructureElement(Environment env) {
0136: this (env, null, null);
0137: }
0138:
0139: /**
0140: * Creates a new structure element with no parent and given children.
0141: *
0142: * @param env the environment
0143: * @param children the children
0144: */
0145: public StructureElement(Environment env, StructureElement[] children) {
0146: this (env, null, children);
0147: }
0148:
0149: /**
0150: * Creates a structure element with no environment and no parent and children. The environment
0151: * should be defined with the method {@link
0152: * jacareto.system.EnvironmentMember#setEnvironment(Environment)} before environment instances
0153: * will be accessed.
0154: */
0155: public StructureElement() {
0156: this (null, null, null);
0157: }
0158:
0159: /**
0160: * Parses a record which is tokenized by the given record tokenizer. This method always returns
0161: * <code>null</code> at the moment; it must be overwritten in subclasses. When this method
0162: * returns, the recordTokenizer must point at the position of the record which should be
0163: * parsed next. So, if the method returns a StructureElement, the tokenizer must point at the
0164: * first recordable after the last recordable contained in the structure element. If the
0165: * method returns <code>null</code>, the tokenizer must point at the position it has pointed
0166: * at when the method has been invoked.
0167: *
0168: * @param env the environment
0169: * @param recordTokenizer the record tokenizer
0170: *
0171: * @return DOCUMENT ME!
0172: */
0173: public static StructureElement parse(Environment env,
0174: RecordTokenizer recordTokenizer) {
0175: return null;
0176: }
0177:
0178: /**
0179: * Returns the parent of a structure element.
0180: *
0181: * @return the parent, or <code>null</code> if there is no parent
0182: */
0183: public TreeNode getParent() {
0184: return parent;
0185: }
0186:
0187: /**
0188: * Sets the structure this element belongs to.
0189: *
0190: * @param structure the structure
0191: */
0192: public void setStructure(Structure structure) {
0193: if (structure != null) {
0194: removeStructureElementListener(structure);
0195: }
0196:
0197: this .structure = structure;
0198:
0199: Iterator it = childrenIterator();
0200:
0201: while (it.hasNext()) {
0202: ((StructureElement) it.next()).setStructure(structure);
0203: }
0204:
0205: if (structure != null) {
0206: addStructureElementListener(structure);
0207: }
0208: }
0209:
0210: /**
0211: * Returns the structure this element belongs to.
0212: *
0213: * @return the structure
0214: */
0215: public Structure getStructure() {
0216: return structure;
0217: }
0218:
0219: /**
0220: * Sets the parent for this structure element.
0221: *
0222: * @param parent {@link StructureElement}
0223: */
0224: public void setParent(StructureElement parent) {
0225: this .parent = parent;
0226: }
0227:
0228: /**
0229: * Returns an iterator on the children of the structure element.
0230: *
0231: * @return {@link Iterator}
0232: */
0233: public Iterator childrenIterator() {
0234: return children.iterator();
0235: }
0236:
0237: /**
0238: * Returns the children as array.
0239: *
0240: * @return DOCUMENT ME!
0241: */
0242: public StructureElement[] getChildren() {
0243: StructureElement[] result = new StructureElement[children
0244: .size()];
0245:
0246: for (int i = 0; i < result.length; i++) {
0247: result[i] = (StructureElement) children.get(i);
0248: }
0249:
0250: return result;
0251: }
0252:
0253: /**
0254: * Sets the children. Also sets this instance as parent for all children, if the specified
0255: * array is not <code>null</code>.
0256: *
0257: * @param children StructureElement[]
0258: */
0259: public void setChildren(StructureElement[] children) {
0260: if (children != null) {
0261: this .children = new Vector(Arrays.asList(children));
0262:
0263: for (int i = 0; i < children.length; i++) {
0264: children[i].setParent(this );
0265: }
0266: } else {
0267: this .children = new Vector(5, 5);
0268: }
0269: }
0270:
0271: /**
0272: * Returns the child at the specified index.
0273: *
0274: * @param index the index of the child
0275: *
0276: * @return the child at index <code>index</code>
0277: */
0278: public StructureElement getChild(int index) {
0279: return (StructureElement) children.get(index);
0280: }
0281:
0282: /**
0283: * Returns the index of a given child.
0284: *
0285: * @param child the child we are interested in
0286: *
0287: * @return the index, or -1 if the child is not contained
0288: */
0289: public int getIndex(TreeNode child) {
0290: return children.indexOf(child);
0291: }
0292:
0293: /**
0294: * Returns the number of the children.
0295: *
0296: * @return DOCUMENT ME!
0297: */
0298: public int getChildrenCount() {
0299: return children.size();
0300: }
0301:
0302: /**
0303: * Calculates the duration of the structure element. If the structure element has children, the
0304: * duration is the sum of the children's durations. Otherwise this it is 0 by default and
0305: * should be overwritten in subclasses if necessary. This method also calls the
0306: * <code>calculateDuration</code> method of its children.
0307: */
0308: public void calculateDuration() {
0309: duration = 0;
0310:
0311: if (hasChildren()) {
0312: for (int i = 0; i < children.size(); i++) {
0313: StructureElement child = (StructureElement) children
0314: .get(i);
0315: child.calculateDuration();
0316: duration += child.getDuration();
0317: }
0318: } else {
0319: duration = 0;
0320: }
0321: }
0322:
0323: /**
0324: * Returns the processing time of the structure element, if it has one, else 0 is returned.
0325: *
0326: * @return the processing time, in msecs
0327: */
0328: public long getProcTime() {
0329: return procTime;
0330: }
0331:
0332: /**
0333: * Sets the processing time of the structure element. Takes the average processing time. If the
0334: * average processing time is longer than the duration, the duration is set as proc time.
0335: *
0336: * @param processingTime the new processing time in msecs
0337: */
0338: public void setProcTime(long processingTime) {
0339: // only set if element has a processing time
0340: if (this .hasProcTime()) {
0341: // calculate current processing time average
0342: this .procTime = calculateProcTime(processingTime);
0343:
0344: // check, if duration is smaller than procTime
0345: if (this .procTime > this .getDuration()) {
0346: this .procTime = this .getDuration();
0347: }
0348: }
0349: }
0350:
0351: /**
0352: * True, if this StructureElement has a processing time or is needed just for structuring
0353: * purposes.
0354: *
0355: * @return boolean
0356: */
0357: public boolean hasProcTime() {
0358: return true;
0359: }
0360:
0361: /**
0362: * <p>
0363: * Method which calculates the correct processing time in this session.
0364: * </p>
0365: *
0366: * <p>
0367: * Therefore it uses the {@link #procTimes} List which holds all measured processing times in
0368: * this session.
0369: * </p>
0370: *
0371: * @param currentProcTime the currently measured processing time
0372: *
0373: * @return long the average processing time of this event
0374: */
0375: private long calculateProcTime(long currentProcTime) {
0376: long sum = currentProcTime;
0377: long calculatedTime = currentProcTime;
0378: int counter = 1;
0379:
0380: if (procTimes.size() > 0) {
0381: for (int i = 0; i < procTimes.size(); i++) {
0382: sum += ((Long) procTimes.get(i)).longValue();
0383: counter++;
0384: }
0385:
0386: calculatedTime = sum / counter;
0387: }
0388:
0389: return calculatedTime;
0390: }
0391:
0392: /**
0393: * Returns the duration of the structure element
0394: *
0395: * @return the duration, in msec
0396: */
0397: public long getDuration() {
0398: return duration;
0399: }
0400:
0401: /**
0402: * Sets the duration. Should be used in the {@link StructureElement#calculateDuration()} method
0403: * of subclasses. The duration has to be the proctime minimum
0404: *
0405: * @param duration the new duration
0406: */
0407: public void setDuration(long duration) {
0408: if (duration > 0) {
0409: this .duration = duration;
0410: } else {
0411: this .duration = 0;
0412: }
0413: }
0414:
0415: /**
0416: * Returns all leaf nodes of the node's subtree. The order of the recordables is the order
0417: * defined by the structure and NOT the original order in the Record;
0418: *
0419: * @return the recordables as array
0420: */
0421: public Recordable[] getRecordables() {
0422: Vector vector = new Vector(5, 5);
0423: addRecordablesToVector(vector);
0424:
0425: Recordable[] result = new Recordable[vector.size()];
0426:
0427: for (int i = 0; i < result.length; i++) {
0428: result[i] = (Recordable) vector.get(i);
0429: }
0430:
0431: return result;
0432: }
0433:
0434: /**
0435: * Returns all leaf nodes of the node's subtree. The order of the recordables is the original
0436: * order in the given record.
0437: *
0438: * @param record the record
0439: *
0440: * @return the recordables as array
0441: */
0442: public Recordable[] getRecordablesInRecordOrder(
0443: ReadAccessRecord record) {
0444: Recordable[] result = null;
0445:
0446: try {
0447: Recordable[] recordablesInStructureOrder = getRecordables();
0448:
0449: TreeMap indexToRecordable = new TreeMap();
0450:
0451: for (int i = 0; i < recordablesInStructureOrder.length; i++) {
0452: Recordable recordable = recordablesInStructureOrder[i];
0453: indexToRecordable.put("" + record.getIndex(recordable),
0454: recordable);
0455: }
0456:
0457: result = new Recordable[recordablesInStructureOrder.length];
0458:
0459: Set keySet = indexToRecordable.keySet();
0460: Iterator it = keySet.iterator();
0461: int index = 0;
0462:
0463: while (it.hasNext()) {
0464: result[index++] = (Recordable) indexToRecordable.get(it
0465: .next());
0466: }
0467: } catch (RecordException rex) {
0468: result = null;
0469: }
0470:
0471: return result;
0472: }
0473:
0474: /**
0475: * Returns all leaf nodes of the node's subtree. The order of the recordables is the original
0476: * order in the structure's record this structure element belongs to.
0477: *
0478: * @return the recordables as array
0479: */
0480: public Recordable[] getRecordablesInRecordOrder() {
0481: return getRecordablesInRecordOrder(getStructure().getRecord());
0482: }
0483:
0484: /**
0485: * Adds all leave nodes belonging to the node's subtree to the given vector. If the actual
0486: * instance is a recordable, it adds itself to the vector.
0487: *
0488: * @param v DOCUMENT ME!
0489: */
0490: protected void addRecordablesToVector(Vector v) {
0491: if (this instanceof Recordable) {
0492: v.add(this );
0493: } else {
0494: Iterator it = childrenIterator();
0495:
0496: while (it.hasNext()) {
0497: StructureElement element = (StructureElement) it.next();
0498: element.addRecordablesToVector(v);
0499: }
0500: }
0501: }
0502:
0503: /**
0504: * Adds a child to the current children.
0505: *
0506: * @param child the child to add
0507: */
0508: public void addChild(StructureElement child) {
0509: children.add(child);
0510: child.setParent(this );
0511: child.setStructure(structure);
0512:
0513: // Fire event
0514: int[] indices = new int[1];
0515: indices[0] = getIndex(child);
0516:
0517: StructureElement[] addedChildren = new StructureElement[1];
0518: addedChildren[0] = child;
0519: fireStructureElementEvent(new StructureElementEvent(this ,
0520: StructureElementEvent.CHILDREN_INSERTED, addedChildren,
0521: indices));
0522: }
0523:
0524: /**
0525: * Adds an array of children.
0526: *
0527: * @param children DOCUMENT ME!
0528: */
0529: public void addChildren(StructureElement[] children) {
0530: // Create instances for event
0531: int[] indices = new int[children.length];
0532: StructureElement[] addedChildren = new StructureElement[children.length];
0533:
0534: for (int i = 0; i < children.length; i++) {
0535: StructureElement child = children[i];
0536: this .children.add(child);
0537: child.setParent(this );
0538: child.setStructure(structure);
0539: indices[i] = getIndex(child);
0540: addedChildren[i] = child;
0541: }
0542:
0543: // Fire event
0544: fireStructureElementEvent(new StructureElementEvent(this ,
0545: StructureElementEvent.CHILDREN_INSERTED, addedChildren,
0546: indices));
0547: }
0548:
0549: /**
0550: * Inserts a child at a given index.
0551: *
0552: * @param child the child to add
0553: * @param index the index
0554: */
0555: public void insertChild(StructureElement child, int index) {
0556: children.add(index, child);
0557: child.setParent(this );
0558: child.setStructure(structure);
0559:
0560: // Fire event
0561: int[] indices = new int[1];
0562: indices[0] = index;
0563:
0564: StructureElement[] addedChildren = new StructureElement[1];
0565: addedChildren[0] = child;
0566: fireStructureElementEvent(new StructureElementEvent(this ,
0567: StructureElementEvent.CHILDREN_INSERTED, addedChildren,
0568: indices));
0569: }
0570:
0571: /**
0572: * Removes a child. Does not remove the leaf nodes from the record; this must be done at
0573: * another location.
0574: *
0575: * @param child the child to remove
0576: */
0577: public void removeChild(StructureElement child) {
0578: if (children.contains(child)) {
0579: // Create instances for event
0580: int[] index = new int[1];
0581: index[0] = getIndex(child);
0582:
0583: StructureElement[] removedChildren = new StructureElement[1];
0584: removedChildren[0] = child;
0585:
0586: // remove element
0587: children.removeElement(child);
0588: child.setParent(null);
0589: child.setStructure(null);
0590:
0591: // fire event
0592: fireStructureElementEvent(new StructureElementEvent(this ,
0593: StructureElementEvent.CHILDREN_REMOVED,
0594: removedChildren, index));
0595: }
0596: }
0597:
0598: /**
0599: * Removes this node from its parent. Does not remove the leaf nodes from the record; this must
0600: * be done at another location.
0601: */
0602: public void removeFromParent() {
0603: if (parent != null) {
0604: parent.removeChild(this );
0605: }
0606: }
0607:
0608: /**
0609: * Removes all children. Does not remove the leaf nodes from the record; this must be done at
0610: * another location.
0611: */
0612: public void removeAllChildren() {
0613: Iterator it = childrenIterator();
0614:
0615: // Create instances for event
0616: int[] indices = new int[getChildrenCount()];
0617: StructureElement[] removedChildren = new StructureElement[getChildrenCount()];
0618:
0619: // remove elements
0620: int index = 0;
0621:
0622: while (it.hasNext()) {
0623: StructureElement child = (StructureElement) it.next();
0624: child.setParent(null);
0625: child.setStructure(null);
0626: indices[index] = index;
0627: removedChildren[index] = child;
0628: index++;
0629: }
0630:
0631: children.clear();
0632:
0633: // fire event
0634: fireStructureElementEvent(new StructureElementEvent(this ,
0635: StructureElementEvent.CHILDREN_REMOVED,
0636: removedChildren, indices));
0637: }
0638:
0639: /**
0640: * Returns whether or not this element has children.
0641: *
0642: * @return DOCUMENT ME!
0643: */
0644: public boolean hasChildren() {
0645: return (children != null) && (children.size() > 0);
0646: }
0647:
0648: /**
0649: * Returns the root of this element. If there is no element of type <code>RootElement</code>,
0650: * then <code>null</code> will be returned.
0651: *
0652: * @return DOCUMENT ME!
0653: */
0654: public RootElement getRoot() {
0655: StructureElement result = this ;
0656:
0657: while (result.getParent() != null) {
0658: result = (StructureElement) result.getParent();
0659: }
0660:
0661: if (result instanceof RootElement) {
0662: return (RootElement) result;
0663: } else {
0664: return null;
0665: }
0666: }
0667:
0668: /**
0669: * Returns the name of the element.
0670: *
0671: * @return the name
0672: */
0673: public abstract String getElementName();
0674:
0675: /**
0676: * Returns a description of the element.
0677: *
0678: * @return the description
0679: */
0680: public abstract String getElementDescription();
0681:
0682: /**
0683: * Returns a String which describes the content of the element shortly.
0684: *
0685: * @return a string with a short description of the element
0686: */
0687: public abstract String toShortString();
0688:
0689: /**
0690: * Converts a vector of structure elements to an array of structure elements
0691: *
0692: * @param v Vector to convert
0693: *
0694: * @return the array containing all structure elements, or <code>null</code> if the vector is
0695: * not convertable.
0696: */
0697: public static StructureElement[] vectorToArray(Vector v) {
0698: StructureElement[] result = null;
0699:
0700: try {
0701: result = new StructureElement[v.size()];
0702:
0703: for (int i = 0; i < result.length; i++) {
0704: result[i] = (StructureElement) v.get(i);
0705: }
0706: } catch (Throwable t) {
0707: ;
0708: }
0709:
0710: return result;
0711: }
0712:
0713: /**
0714: * Returns all children of the structure element which are instances of the given class, in
0715: * depth first search order.
0716: *
0717: * @param className DOCUMENT ME!
0718: *
0719: * @return DOCUMENT ME!
0720: */
0721: public StructureElement[] getChildren(String className) {
0722: StructureElement[] result;
0723:
0724: try {
0725: ClassFilter classFilter = new ClassFilter(env, className);
0726: result = classFilter.getElements(this , true);
0727: } catch (ClassNotFoundException c) {
0728: result = new StructureElement[0];
0729: }
0730:
0731: return result;
0732: }
0733:
0734: /**
0735: * Returns true is this structure element is an user structure ?
0736: *
0737: * @return whether or not the element is static
0738: */
0739: public boolean isStatic() {
0740: return isStatic;
0741: }
0742:
0743: //------------------------------------------------------
0744: // listener methods
0745: //------------------------------------------------------
0746:
0747: /**
0748: * Adds a structure element listener
0749: *
0750: * @param listener the listener
0751: */
0752: public void addStructureElementListener(
0753: StructureElementListener listener) {
0754: if (!structureElementListeners.contains(listener)) {
0755: structureElementListeners.add(listener);
0756: }
0757: }
0758:
0759: /**
0760: * Removes a structure element listener
0761: *
0762: * @param listener the listener
0763: */
0764: public void removeStructureElementListener(
0765: StructureElementListener listener) {
0766: if (structureElementListeners.contains(listener)) {
0767: structureElementListeners.remove(listener);
0768: }
0769: }
0770:
0771: /**
0772: * Sets whether or not the element notifies listeners of changes.
0773: *
0774: * @param isNotificationEnabled whether or not the notification is enabled
0775: */
0776: public void setNotificationEnabled(boolean isNotificationEnabled) {
0777: this .isNotificationEnabled = isNotificationEnabled;
0778: }
0779:
0780: /**
0781: * Fires a structure element event to all listeners.
0782: *
0783: * @param event the event
0784: */
0785: protected void fireStructureElementEvent(StructureElementEvent event) {
0786: // Logger.getLogger(this.getClass()).debug("StructureElement.fireStructureElementEvent(): " + event.getID());
0787: if (isNotificationEnabled) {
0788: Iterator it = structureElementListeners.iterator();
0789:
0790: while (it.hasNext()) {
0791: ((StructureElementListener) it.next())
0792: .structureElementChanged(event);
0793: }
0794: }
0795: }
0796:
0797: /**
0798: * Fires a structure element event to all listeners which indicates that one or more values
0799: * have been changed.
0800: */
0801: protected void fireValuesChanged() {
0802: StructureElement parent = (StructureElement) getParent();
0803:
0804: if (parent != null) {
0805: int[] indices = new int[1];
0806: indices[0] = parent.getIndex(this );
0807:
0808: StructureElement[] children = new StructureElement[1];
0809: children[0] = this ;
0810: fireStructureElementEvent(new StructureElementEvent(parent,
0811: StructureElementEvent.VALUES_CHANGED, children,
0812: indices));
0813: } else {
0814: fireStructureElementEvent(new StructureElementEvent(this ,
0815: StructureElementEvent.VALUES_CHANGED, null, null));
0816: }
0817: }
0818:
0819: //------------------------------------------------------
0820: // methods of TreeNode
0821: //------------------------------------------------------
0822:
0823: /**
0824: * Returns the children of the receiver as enumeration
0825: *
0826: * @return the enumeration
0827: */
0828: public Enumeration children() {
0829: return children.elements();
0830: }
0831:
0832: /**
0833: * Returns <code>true</code> if the receiver allows children.
0834: *
0835: * @return whether or not the receiver allows children
0836: */
0837: public boolean getAllowsChildren() {
0838: // Should be improved; not every node allows children
0839: return true;
0840: }
0841:
0842: /**
0843: * Returns the child at index <code>index</code>
0844: *
0845: * @param index the index of the child
0846: *
0847: * @return the child
0848: */
0849: public TreeNode getChildAt(int index) {
0850: return getChild(index);
0851: }
0852:
0853: /**
0854: * Returns the number of children.
0855: *
0856: * @return the number of childen
0857: */
0858: public int getChildCount() {
0859: return getChildrenCount();
0860: }
0861:
0862: /**
0863: * Returns <code>true</code> if the receiver is a leaf
0864: *
0865: * @return whether or not the receiver is a leaf
0866: */
0867: public boolean isLeaf() {
0868: // should be improved
0869: return !hasChildren();
0870: }
0871:
0872: /**
0873: * Sets the children of the structure element without setting attribute values of the children.
0874: * This method should just be used when structure elements are to be grouped together without
0875: * building a "real" structure element.
0876: *
0877: * @param children DOCUMENT ME!
0878: */
0879: public void setChildrenCursorily(StructureElement[] children) {
0880: this .children = new Vector(5, 5);
0881:
0882: for (int i = 0; i < children.length; i++) {
0883: this .children.add(children[i]);
0884: }
0885: }
0886:
0887: /**
0888: * Removes the children which have been set with the method {@link
0889: * #setChildrenCursorily(StructureElement[])}
0890: */
0891: public void removeChildrenCursorily() {
0892: this .children = null;
0893: }
0894:
0895: /**
0896: * Clones a structure element. Should be overwritten in subclasses
0897: *
0898: * @return DOCUMENT ME!
0899: */
0900: public Object clone() {
0901: Object result = null;
0902:
0903: try {
0904: result = super .clone();
0905: ((StructureElement) result).structureElementListeners = new Vector(
0906: 5, 5);
0907: } catch (CloneNotSupportedException cnse) {
0908: getLogger().error(cnse);
0909: }
0910:
0911: return result;
0912: }
0913:
0914: /**
0915: * Returns a vector of all cloned children.
0916: *
0917: * @return DOCUMENT ME!
0918: */
0919: public StructureElement[] getClonedChildren() {
0920: StructureElement[] result = null;
0921:
0922: if (children.size() > 0) {
0923: result = new StructureElement[children.size()];
0924:
0925: Iterator it = children.iterator();
0926: int index = 0;
0927:
0928: while (it.hasNext()) {
0929: result[index++] = (StructureElement) ((StructureElement) it
0930: .next()).clone();
0931: }
0932: }
0933:
0934: return result;
0935: }
0936:
0937: /**
0938: * Returns the UUID String
0939: *
0940: * @return Returns the UUIDString.
0941: */
0942: public String getUUIDString() {
0943: if (this .UUIDString == null) {
0944: this .UUIDString = UUIDGen.getInstance().generateUUID();
0945: }
0946:
0947: return this .UUIDString;
0948: }
0949:
0950: /**
0951: * Sets the UUID String for this StructureElement. Can only be set once!
0952: *
0953: * @param string The UUIDString to set.
0954: */
0955: public void setUUIDString(String string) {
0956: Validate.isTrue(this .UUIDString == null);
0957:
0958: UUIDString = string;
0959: }
0960:
0961: //------------------------------------------------------
0962: // search methods
0963: //------------------------------------------------------
0964:
0965: /**
0966: * Sets whether or not this structure element is a search hit.
0967: *
0968: * @param isSearchHit guess!
0969: */
0970: private void setIsSearchHit(boolean isSearchHit) {
0971: this .isSearchHit = isSearchHit;
0972: }
0973:
0974: /**
0975: * Sets whether or not this structure element is a parent element of a search hit.
0976: *
0977: * @param isParentOfSearchHit guess!
0978: */
0979: private void setIsParentOfSearchHit(boolean isParentOfSearchHit) {
0980: this .isParentOfSearchHit = isParentOfSearchHit;
0981: }
0982:
0983: /**
0984: * Returns whether or not the element is a search hit.
0985: *
0986: * @return <code>true</code> if the element is a search hit, otherwise <code>false</code>
0987: */
0988: public boolean isSearchHit() {
0989: return isSearchHit;
0990: }
0991:
0992: /**
0993: * Returns whether or not the element is a parent element of a search hit.
0994: *
0995: * @return <code>true</code> if the element is a parent, otherwise <code>false</code>
0996: */
0997: public boolean isParentOfSearchHit() {
0998: return isParentOfSearchHit;
0999: }
1000:
1001: /**
1002: * Returns a string which is the basis for the search. The default method returns the result of
1003: * {@link #toShortString()} and can be overwritten in subclasses.
1004: *
1005: * @return the search string
1006: */
1007: protected String getSearchString() {
1008: return toShortString();
1009: }
1010:
1011: /**
1012: * Searches for a string (regular expression match or string comparison). If the regular
1013: * expression matches or if the search string in contained, the element is marked as search
1014: * hit. If a child element is a hit and not this instance, the structure element is marked as
1015: * a parent element of a search hit. Either the regular expression is used or the
1016: * searchstring. One of the two parameters should be <code>null</code>
1017: *
1018: * @param re the regular expression
1019: * @param searchString the searchString
1020: *
1021: * @return the number of hits in this structure subtree
1022: */
1023: public int search(RE re, String searchString) {
1024: int numberOfHits = 0;
1025:
1026: String myOwnString = getSearchString();
1027: boolean isRegexpSearch = (re != null);
1028:
1029: boolean isSearchHit = isRegexpSearch ? re
1030: .match(getSearchString()) : (myOwnString
1031: .indexOf(searchString) != -1);
1032:
1033: if (isSearchHit) {
1034: numberOfHits++;
1035: }
1036:
1037: boolean isParentOfSearchHit = false;
1038:
1039: if (hasChildren()) {
1040: Iterator it = childrenIterator();
1041:
1042: while (it.hasNext()) {
1043: int childNumberOfHits = ((StructureElement) it.next())
1044: .search(re, searchString);
1045: isParentOfSearchHit = isParentOfSearchHit
1046: || (childNumberOfHits > 0);
1047: numberOfHits += childNumberOfHits;
1048: }
1049: }
1050:
1051: setIsSearchHit(isSearchHit);
1052: setIsParentOfSearchHit(isParentOfSearchHit);
1053:
1054: return numberOfHits;
1055: }
1056:
1057: /**
1058: * Clears the search results.
1059: */
1060: public void clearSearchResults() {
1061: setIsSearchHit(false);
1062: setIsParentOfSearchHit(false);
1063:
1064: if (hasChildren()) {
1065: Iterator it = childrenIterator();
1066:
1067: while (it.hasNext()) {
1068: ((StructureElement) it.next()).clearSearchResults();
1069: }
1070: }
1071: }
1072: }
|