001: /**
002: * Copyright (c) 2003-2005, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.pdfviewer;
031:
032: /**
033: * A tree model that uses a cos document.
034: *
035: *
036: * @author wurtz
037: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
038: * @version $Revision: 1.9 $
039: */
040: import javax.swing.tree.TreePath;
041: import javax.swing.tree.TreeModel;
042:
043: //import java.awt.event.*;
044: import javax.swing.event.TreeModelListener;
045:
046: import org.pdfbox.cos.COSArray;
047: import org.pdfbox.cos.COSBase;
048: import org.pdfbox.cos.COSDictionary;
049: import org.pdfbox.cos.COSDocument;
050: import org.pdfbox.cos.COSName;
051: import org.pdfbox.cos.COSObject;
052:
053: import org.pdfbox.pdmodel.PDDocument;
054:
055: import java.util.Collections;
056: import java.util.List;
057:
058: /**
059: * A class to model a PDF document as a tree structure.
060: *
061: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
062: * @version $Revision: 1.9 $
063: */
064: public class PDFTreeModel implements TreeModel {
065: private PDDocument document;
066:
067: /**
068: * constructor.
069: */
070: public PDFTreeModel() {
071: //default constructor
072: }
073:
074: /**
075: * Constructor to take a document.
076: *
077: * @param doc The document to display in the tree.
078: */
079: public PDFTreeModel(PDDocument doc) {
080: setDocument(doc);
081: }
082:
083: /**
084: * Set the document to display in the tree.
085: *
086: * @param doc The document to display in the tree.
087: */
088: public void setDocument(PDDocument doc) {
089: document = doc;
090: }
091:
092: /**
093: * Adds a listener for the <code>TreeModelEvent</code>
094: * posted after the tree changes.
095: *
096: * @param l the listener to add
097: * @see #removeTreeModelListener
098: *
099: */
100: public void addTreeModelListener(TreeModelListener l) {
101: //required for interface
102: }
103:
104: /**
105: * Returns the child of <code>parent</code> at index <code>index</code>
106: * in the parent's
107: * child array. <code>parent</code> must be a node previously obtained
108: * from this data source. This should not return <code>null</code>
109: * if <code>index</code>
110: * is a valid index for <code>parent</code> (that is <code>index >= 0 &&
111: * index < getChildCount(parent</code>)).
112: *
113: * @param parent a node in the tree, obtained from this data source
114: * @param index The index into the parent object to location the child object.
115: * @return the child of <code>parent</code> at index <code>index</code>
116: *
117: */
118: public Object getChild(Object parent, int index) {
119: Object retval = null;
120: if (parent instanceof COSArray) {
121: ArrayEntry entry = new ArrayEntry();
122: entry.setIndex(index);
123: entry.setValue(((COSArray) parent).getObject(index));
124: retval = entry;
125: } else if (parent instanceof COSDictionary) {
126: COSDictionary dict = ((COSDictionary) parent);
127: List keys = dict.keyList();
128: Collections.sort(keys);
129: Object key = keys.get(index);
130: Object value = dict.getDictionaryObject((COSName) key);
131: MapEntry entry = new MapEntry();
132: entry.setKey(key);
133: entry.setValue(value);
134: retval = entry;
135: } else if (parent instanceof MapEntry) {
136: retval = getChild(((MapEntry) parent).getValue(), index);
137: } else if (parent instanceof ArrayEntry) {
138: retval = getChild(((ArrayEntry) parent).getValue(), index);
139: } else if (parent instanceof COSDocument) {
140: retval = ((COSDocument) parent).getObjects().get(index);
141: } else if (parent instanceof COSObject) {
142: retval = ((COSObject) parent).getObject();
143: } else {
144: throw new RuntimeException("Unknown COS type "
145: + parent.getClass().getName());
146: }
147: return retval;
148: }
149:
150: /** Returns the number of children of <code>parent</code>.
151: * Returns 0 if the node
152: * is a leaf or if it has no children. <code>parent</code> must be a node
153: * previously obtained from this data source.
154: *
155: * @param parent a node in the tree, obtained from this data source
156: * @return the number of children of the node <code>parent</code>
157: *
158: */
159: public int getChildCount(Object parent) {
160: int retval = 0;
161: if (parent instanceof COSArray) {
162: retval = ((COSArray) parent).size();
163: } else if (parent instanceof COSDictionary) {
164: retval = ((COSDictionary) parent).size();
165: } else if (parent instanceof MapEntry) {
166: retval = getChildCount(((MapEntry) parent).getValue());
167: } else if (parent instanceof ArrayEntry) {
168: retval = getChildCount(((ArrayEntry) parent).getValue());
169: } else if (parent instanceof COSDocument) {
170: retval = ((COSDocument) parent).getObjects().size();
171: } else if (parent instanceof COSObject) {
172: retval = 1;
173: }
174: return retval;
175: }
176:
177: /** Returns the index of child in parent. If <code>parent</code>
178: * is <code>null</code> or <code>child</code> is <code>null</code>,
179: * returns -1.
180: *
181: * @param parent a note in the tree, obtained from this data source
182: * @param child the node we are interested in
183: * @return the index of the child in the parent, or -1 if either
184: * <code>child</code> or <code>parent</code> are <code>null</code>
185: *
186: */
187: public int getIndexOfChild(Object parent, Object child) {
188: int retval = -1;
189: if (parent != null && child != null) {
190: if (parent instanceof COSArray) {
191: COSArray array = (COSArray) parent;
192: if (child instanceof ArrayEntry) {
193: ArrayEntry arrayEntry = (ArrayEntry) child;
194: retval = arrayEntry.getIndex();
195: } else {
196: retval = array.indexOf((COSBase) child);
197: }
198: } else if (parent instanceof COSDictionary) {
199: MapEntry entry = (MapEntry) child;
200: COSDictionary dict = (COSDictionary) parent;
201: List keys = dict.keyList();
202: Collections.sort(keys);
203: for (int i = 0; retval == -1 && i < keys.size(); i++) {
204: if (keys.get(i).equals(entry.getKey())) {
205: retval = i;
206: }
207: }
208: } else if (parent instanceof MapEntry) {
209: retval = getIndexOfChild(
210: ((MapEntry) parent).getValue(), child);
211: } else if (parent instanceof ArrayEntry) {
212: retval = getIndexOfChild(((ArrayEntry) parent)
213: .getValue(), child);
214: } else if (parent instanceof COSDocument) {
215: retval = ((COSDocument) parent).getObjects().indexOf(
216: child);
217: } else if (parent instanceof COSObject) {
218: retval = 0;
219: } else {
220: throw new RuntimeException("Unknown COS type "
221: + parent.getClass().getName());
222: }
223: }
224: return retval;
225: }
226:
227: /** Returns the root of the tree. Returns <code>null</code>
228: * only if the tree has no nodes.
229: *
230: * @return the root of the tree
231: *
232: */
233: public Object getRoot() {
234: return document.getDocument().getTrailer();
235: }
236:
237: /** Returns <code>true</code> if <code>node</code> is a leaf.
238: * It is possible for this method to return <code>false</code>
239: * even if <code>node</code> has no children.
240: * A directory in a filesystem, for example,
241: * may contain no files; the node representing
242: * the directory is not a leaf, but it also has no children.
243: *
244: * @param node a node in the tree, obtained from this data source
245: * @return true if <code>node</code> is a leaf
246: *
247: */
248: public boolean isLeaf(Object node) {
249: boolean isLeaf = !(node instanceof COSDictionary
250: || node instanceof COSArray
251: || node instanceof COSDocument
252: || node instanceof COSObject
253: || (node instanceof MapEntry && !isLeaf(((MapEntry) node)
254: .getValue())) || (node instanceof ArrayEntry && !isLeaf(((ArrayEntry) node)
255: .getValue())));
256: return isLeaf;
257: }
258:
259: /** Removes a listener previously added with
260: * <code>addTreeModelListener</code>.
261: *
262: * @see #addTreeModelListener
263: * @param l the listener to remove
264: *
265: */
266:
267: public void removeTreeModelListener(TreeModelListener l) {
268: //required for interface
269: }
270:
271: /** Messaged when the user has altered the value for the item identified
272: * by <code>path</code> to <code>newValue</code>.
273: * If <code>newValue</code> signifies a truly new value
274: * the model should post a <code>treeNodesChanged</code> event.
275: *
276: * @param path path to the node that the user has altered
277: * @param newValue the new value from the TreeCellEditor
278: *
279: */
280: public void valueForPathChanged(TreePath path, Object newValue) {
281: //required for interface
282: }
283: }
|