001: /*
002: * Jacareto Copyright (c) 2002-2005
003: * Applied Computer Science Research Group, Darmstadt University of
004: * Technology, Institute of Mathematics & Computer Science,
005: * Ludwigsburg University of Education, and Computer Based
006: * Learning Research Group, Aachen University. All rights reserved.
007: *
008: * Jacareto is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation; either
011: * version 2 of the License, or (at your option) any later version.
012: *
013: * Jacareto is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public
019: * License along with Jacareto; if not, write to the Free
020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: *
022: */
023:
024: package jacareto.struct;
025:
026: import jacareto.parse.RecordTokenizer;
027: import jacareto.record.ReadAccessRecord;
028: import jacareto.record.RecordChangeEvent;
029: import jacareto.record.RecordChangeListener;
030: import jacareto.record.RecordException;
031: import jacareto.record.Recordable;
032: import jacareto.struct.event.StructureElementEvent;
033: import jacareto.struct.event.StructureElementListener;
034: import jacareto.system.Environment;
035: import jacareto.system.EnvironmentMember;
036:
037: import org.apache.regexp.RE;
038:
039: import java.util.Iterator;
040: import java.util.Vector;
041:
042: import javax.swing.event.TreeModelEvent;
043: import javax.swing.event.TreeModelListener;
044: import javax.swing.tree.TreeModel;
045: import javax.swing.tree.TreeNode;
046: import javax.swing.tree.TreePath;
047:
048: /**
049: * This is a record structure. Structures can only be built on records which allow read access.
050: *
051: * @author <a href="mailto:cspannagel@web.de">Christian Spannagel</a>
052: * @version 1.03
053: */
054: public class Structure extends EnvironmentMember implements
055: RecordChangeListener, StructureElementListener, TreeModel {
056: /** The record. */
057: private ReadAccessRecord record;
058:
059: /** The root element of the structure. */
060: private RootElement rootElement;
061:
062: /** The tree model listeners. */
063: private Vector treeModelListeners;
064:
065: /**
066: * Creates a new record structure of the given record. The record will be parsed to create the
067: * structure.
068: *
069: * @param env the environment
070: * @param record the record
071: */
072: public Structure(Environment env, ReadAccessRecord record) {
073: super (env);
074: treeModelListeners = new Vector(5, 5);
075:
076: this .record = record;
077: record.addRecordChangeListener(this );
078:
079: RecordTokenizer tokenizer = new RecordTokenizer(env, record);
080: rootElement = (RootElement) RootElement.parse(env, tokenizer);
081: rootElement.setRecord(record);
082: rootElement.setStructure(this );
083: }
084:
085: /**
086: * Creates a structure with a given record and root element. The structure is taken from the
087: * root element, no record will be parsed.
088: *
089: * @param env the environment
090: * @param rootElement the root element
091: * @param record the record
092: */
093: public Structure(Environment env, RootElement rootElement,
094: ReadAccessRecord record) {
095: super (env);
096: treeModelListeners = new Vector(5, 5);
097:
098: this .record = record;
099: record.addRecordChangeListener(this );
100:
101: this .rootElement = rootElement;
102: rootElement.setRecord(record);
103: rootElement.setStructure(this );
104: }
105:
106: /**
107: * Returns the record this structure is based on.
108: *
109: * @return DOCUMENT ME!
110: */
111: public ReadAccessRecord getRecord() {
112: return record;
113: }
114:
115: /**
116: * Returns the root element of the structure.
117: *
118: * @return DOCUMENT ME!
119: */
120: public RootElement getRootElement() {
121: return rootElement;
122: }
123:
124: /**
125: * Clears the structure.
126: */
127: public void clear() {
128: rootElement.removeAllChildren();
129: fireCompleteStructureChange();
130: }
131:
132: /**
133: * Sets the rootElement.
134: *
135: * @param root DOCUMENT ME!
136: */
137: public void setRoot(RootElement root) {
138: if (rootElement != null) {
139: rootElement.setStructure(null);
140: }
141:
142: rootElement = root;
143: rootElement.setStructure(this );
144: fireCompleteStructureChange();
145: }
146:
147: /**
148: * Rebuilds the structure.
149: *
150: * @param tokenizer the record's tokenizer
151: */
152: public void rebuild(RecordTokenizer tokenizer) {
153: /* clear();
154: rootElement = (RootElement) RootElement.parse (env, tokenizer);*/
155: if (rootElement != null) {
156: rootElement.setStructure(null);
157: }
158:
159: rootElement = (RootElement) RootElement.parse(env, rootElement);
160: rootElement.setStructure(this );
161: rootElement.setRecord(record);
162: fireCompleteStructureChange();
163: }
164:
165: // The record change listener method
166:
167: /**
168: * Called when a record change has occured.
169: *
170: * @param event RecordChangeEvent
171: */
172: public void recordHasChanged(RecordChangeEvent event) {
173: switch (event.getID()) {
174: case RecordChangeEvent.RECORDABLES_ADDED:
175: rootElement.addChildren(event.getRecordables());
176:
177: break;
178:
179: case RecordChangeEvent.RECORDABLES_CHANGED:
180:
181: Recordable[] changedRecordables = event.getRecordables();
182:
183: /*for (int i = 0; i < changedRecordables.length; i++) {
184: structureTreeModel.elementChanged (changedRecordables[i]);
185: } */
186: break;
187:
188: case RecordChangeEvent.RECORD_CLEARED:
189: clear();
190:
191: break;
192:
193: case RecordChangeEvent.RECORDABLES_REMOVED:
194:
195: Recordable[] remRecordables = event.getRecordables();
196:
197: for (int i = 0; i < remRecordables.length; i++) {
198: remRecordables[i].removeFromParent();
199: }
200:
201: break;
202:
203: case RecordChangeEvent.RECORDABLES_INSERTED:
204:
205: Recordable[] recordables = event.getRecordables();
206:
207: for (int i = 0; i < recordables.length; i++) {
208: StructureElement parent = (StructureElement) recordables[i]
209: .getParent();
210:
211: if (parent == null) {
212: try {
213: int recIndex = record.getIndex(recordables[i]);
214:
215: if (recIndex == 0) {
216: parent = rootElement;
217: } else {
218: StructureElement elementBefore = record
219: .get(recIndex - 1);
220: parent = (StructureElement) elementBefore
221: .getParent();
222: }
223: } catch (RecordException r) {
224: parent = rootElement;
225: }
226:
227: parent.addChild(recordables[i]);
228: } else {
229: // Element already inserted into the structure; just notify all of the change
230: }
231: }
232:
233: break;
234: }
235: }
236:
237: /**
238: * Called when a structure element event has occured
239: *
240: * @param event the dispatched event
241: */
242: public void structureElementChanged(StructureElementEvent event) {
243: Iterator it = treeModelListeners.iterator();
244:
245: // Creates the tree model event
246: TreePath treePath = getTreePath((TreeNode) event.getSource());
247: TreeModelEvent treeModelEvent = new TreeModelEvent(event
248: .getSource(), treePath, event.getIndices(), event
249: .getChildren());
250:
251: switch (event.getID()) {
252: case StructureElementEvent.CHILDREN_REMOVED:
253:
254: while (it.hasNext()) {
255: ((TreeModelListener) it.next())
256: .treeNodesRemoved(treeModelEvent);
257: }
258:
259: break;
260:
261: case StructureElementEvent.CHILDREN_INSERTED:
262:
263: while (it.hasNext()) {
264: ((TreeModelListener) it.next())
265: .treeNodesInserted(treeModelEvent);
266: }
267:
268: break;
269:
270: case StructureElementEvent.VALUES_CHANGED:
271: default:
272:
273: while (it.hasNext()) {
274: ((TreeModelListener) it.next())
275: .treeNodesChanged(treeModelEvent);
276: }
277:
278: break;
279: }
280: }
281:
282: /**
283: * Returns the tree path to the given node.
284: *
285: * @param node the node
286: *
287: * @return the tree path
288: */
289: public TreePath getTreePath(TreeNode node) {
290: Vector nodes = new Vector(5, 5);
291: nodes.add(node);
292:
293: TreeNode parent = node.getParent();
294:
295: while (parent != null) {
296: nodes.add(parent);
297: parent = parent.getParent();
298: }
299:
300: TreeNode[] pathNodes = new TreeNode[nodes.size()];
301:
302: for (int i = 0; i < pathNodes.length; i++) {
303: pathNodes[i] = (TreeNode) nodes.get(nodes.size() - 1 - i);
304: }
305:
306: return new TreePath(pathNodes);
307: }
308:
309: /**
310: * Fires an event which indicates that the whole structure has changed.
311: */
312: protected void fireCompleteStructureChange() {
313: StructureElement[] children = new StructureElement[1];
314: children[0] = (StructureElement) getRoot();
315:
316: TreeModelEvent treeModelEvent = new TreeModelEvent(this ,
317: children);
318:
319: Iterator it = treeModelListeners.iterator();
320:
321: while (it.hasNext()) {
322: ((TreeModelListener) it.next())
323: .treeStructureChanged(treeModelEvent);
324: }
325: }
326:
327: // ---------------------------------------------------------------------------------
328: // The tree model methods
329: // ---------------------------------------------------------------------------------
330:
331: /**
332: * Adds a tree model listener.
333: *
334: * @param listener the listener to add
335: */
336: public void addTreeModelListener(TreeModelListener listener) {
337: if (!treeModelListeners.contains(listener)) {
338: treeModelListeners.add(listener);
339: }
340: }
341:
342: /**
343: * Removes a tree model listener.
344: *
345: * @param listener the listener to remove
346: */
347: public void removeTreeModelListener(TreeModelListener listener) {
348: if (treeModelListeners.contains(listener)) {
349: treeModelListeners.remove(listener);
350: }
351: }
352:
353: /**
354: * Returns the child of <code>parent</code> at index <code>index</code> in the parent's child
355: * array.
356: *
357: * @param parent the parent node
358: * @param index the index of the child
359: *
360: * @return the child
361: */
362: public Object getChild(Object parent, int index) {
363: return ((StructureElement) parent).getChild(index);
364: }
365:
366: /**
367: * Returns the number of children contained in the element <code>parent</code>
368: *
369: * @param parent the parent node
370: *
371: * @return the number of children in parent
372: */
373: public int getChildCount(Object parent) {
374: return ((StructureElement) parent).getChildrenCount();
375: }
376:
377: /**
378: * Returns the index of the child <code>child</code> in parent <code>parent</code>.
379: *
380: * @param parent the parent node
381: * @param child the child node
382: *
383: * @return the index, or <code>-1</code> if <code>child</code> is not a child of
384: * <code>parent</code>, or if one of the nodes is <code>null</code>
385: */
386: public int getIndexOfChild(Object parent, Object child) {
387: if ((parent == null) || (child == null)) {
388: return -1;
389: }
390:
391: return ((StructureElement) parent)
392: .getIndex((StructureElement) child);
393: }
394:
395: /**
396: * Returns the root. Delegates to {@link #getRootElement ()}
397: *
398: * @return the root
399: */
400: public Object getRoot() {
401: return getRootElement();
402: }
403:
404: /**
405: * Searches for structure (regular expression match or string comparison). If the one of its
406: * children matches the regular expression or contains the given string, <code>true</code> is
407: * return. In addition, all children which are hits are marked. Either the regular expression
408: * is used or the searchstring. One of the two parameters should be <code>null</code>
409: *
410: * @param re the regular expression
411: * @param searchString the searchString
412: *
413: * @return the number of hits in the structure
414: */
415: public int search(RE re, String searchString) {
416: return getRootElement().search(re, searchString);
417: }
418:
419: /**
420: * Clears the search results.
421: */
422: public void clearSearchResults() {
423: getRootElement().clearSearchResults();
424: }
425:
426: /**
427: * Returns whether or not this element is a leaf.
428: *
429: * @param node the node to test
430: *
431: * @return DOCUMENT ME!
432: */
433: public boolean isLeaf(Object node) {
434: // Should be improved; a node which has no children, but is no recordable
435: // is no leaf!
436: return !((StructureElement) node).hasChildren();
437: }
438:
439: /**
440: * Messaged when the user has altered the value for the item identified by <code>path</code> to
441: * <code>newValue</code>.
442: *
443: * @param path DOCUMENT ME!
444: * @param newValue DOCUMENT ME!
445: */
446: public void valueForPathChanged(TreePath path, Object newValue) {
447: System.out.println("not implemented");
448: }
449: }
|