source: other-projects/trunk/gs3-webservices-democlient/src/GS3DemoClient/org/greenstone/gs3client/data/NodeData.java@ 15669

Last change on this file since 15669 was 15669, checked in by ak19, 16 years ago

Corrected logic error in setting NodeData.hasChildren member in constructor

File size: 9.6 KB
Line 
1/**
2 *#########################################################################
3 * NodeData.java - part of the demo-client for Greenstone 3, of the
4 * Greenstone digital library suite from the New Zealand Digital Library
5 * Project at the * University of Waikato, New Zealand.
6 * <BR><BR>
7 * Copyright (C) 2008 New Zealand Digital Library Project
8 * <BR><BR>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 * <BR><BR>
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *########################################################################
19 */
20
21package org.greenstone.gs3client.data;
22
23import java.util.Iterator;
24import java.util.Set;
25import java.util.Map.Entry;
26
27import java.util.Vector;
28import java.util.Map;
29import java.util.HashMap;
30
31import org.w3c.dom.Element;
32import org.w3c.dom.Node;
33import org.w3c.dom.NodeList;
34import org.greenstone.gs3client.data.ClassifierNodeData;
35import org.greenstone.gsdl3.util.GSXML;
36
37/**
38 * Abstract superclass NodeData can represent either a the information in
39 * a &lt;classifierNode&gt; (stored in a ClassifierNodeData object) or in a
40 * &lt;documentNode&gt; (stored in a DocumentNodeData object).
41 * @author ak19
42*/
43public abstract class NodeData {
44 /** The unique nodeID identifying this documentNode or classificationNode */
45 public final String nodeID;
46
47 /** Since we are setting the descendents lazily (only setting the children
48 * each time), we have a little flag to indicate whether this node has
49 * any children. */
50 public final boolean hasChildren;
51 /** stores this NodeData's tag element: can be either
52 * &lt;documentNode&gt; or &lt;classifierNode&gt; */
53 protected final Element nodeTag;
54
55 /** Map of the metadata (name, value) pairs of this document-
56 * or classifier-node */
57 protected Map nodeMetadata = null;
58
59 /** @return an array of the children of this NodeData, however such a
60 * structure may be stored internally by a NodeData subclass. */
61 public abstract NodeData[] getChildren();
62
63 /** Constructor that extracts the nodeID value from the given nodeTag:
64 * &lt;classifierNode nodeID = "x" /&gt;
65 * or &lt;documentNode nodeID = "x" /&gt; */
66 public NodeData(Element nodeTag) {
67 this.nodeTag = nodeTag; // store this node's tag information. Useful
68 // for when we are setting the children of this NodeData
69
70 // We'd definitely have the nodeID attribute, but to be on the safe
71 // side, check it's there first:
72 this.nodeID = nodeTag.hasAttribute(GSXML.NODE_ID_ATT) ?
73 nodeTag.getAttribute(GSXML.NODE_ID_ATT) : "";
74
75 // To determine whether this DocumentNodeData has children, we need
76 // to know whether the DocumentNodeElement it represents has child
77 // DocNodeElements. This kind of information is embedded inside a
78 // nodeStructure element which will often contain a repeat of the
79 // nodeTag-documentNodeElement itself. Therefore, we get the nodeTag's
80 // first descendent element that is a documentNodeElement and check
81 // whether it too has the same nodeID. If so, then we check whether
82 // that documentNodeElement has child nodes - which in that case will
83 // all be documentNodeElements.
84 Element innerDocNode = ParseUtil.getFirstDescElementCalled(
85 nodeTag, GSXML.DOC_NODE_ELEM);
86 if(innerDocNode == null
87 || !innerDocNode.getAttribute(GSXML.NODE_ID_ATT).equals(this.nodeID))
88 {
89 innerDocNode = this.nodeTag;
90 }
91 this.hasChildren = innerDocNode.hasChildNodes();
92 }
93
94 /**
95 * Sets member metadataList if there is a &lt;metadataList&gt; subelement in
96 * docNodeTag: first get the &lt;metadataList&gt; then its &lt;metadata&gt;
97 * children
98 * @param nodeTag is a documentNode or classifierNode XML Element that
99 * contains metadata information which is used to set this NodeData object's
100 * metadata (if there are any, the HashMap nodeMetadata will be set). */
101 public void setMetadataList(Element nodeTag) {
102 NodeList metaList = nodeTag.getElementsByTagName(
103 GSXML.METADATA_ELEM);
104 // will return empty metaList collection if there's none, so check for
105 // size instead of null:
106 int size = metaList.getLength();
107 if(size > 0) {
108 if(nodeMetadata == null) // if we check for this, can call
109 // setMetadataList() on this DocNodeData obj many times
110 // - each time with different <metadata> name-val pairs
111 nodeMetadata = new HashMap(size);
112 for(int i = 0; i < size; i++) {
113 // we know each is an element, as we got all desc elements
114 // of this <documentNode> that are <metadata>
115 Element meta = (Element)metaList.item(i);
116 String name = meta.getAttribute(GSXML.NAME_ATT);
117 Node e = meta.getFirstChild();
118 // If not null, e should be a textNode now: get its value
119 String bodyText = (e == null) ? "" : (String)e.getNodeValue();
120 // nodeMetadata.put(name, bodyText);
121 if(nodeMetadata.containsKey(name)) {
122 // key already exists, get the array of values mapping to that
123 // metadata name (= key)
124 Vector values = (Vector)nodeMetadata.get(name);
125 // check if the last value is the same as
126 // what we're trying to insert
127 String value = (String)values.get(values.size()-1);
128 if(!value.equals(bodyText)) // don't add duplicates
129 // append the new metadata value to the vector v:
130 values.add(bodyText);
131 } else { // create a new (key, val) pair by creating a new vector
132 // (= value) mapping to the new key
133 Vector values = new Vector();
134 values.add(bodyText);
135 nodeMetadata.put(name, values);
136 }
137 }
138 }
139 }
140
141 /** Called when setting descendents lazily: only the children of this
142 * node are set. We don't care to setDescendents now, will set it later
143 * when necessary.
144 * @param v is an empty/non-null Vector which will, at method's end, contain
145 * all the Document- or ClassifierNodeData objects that are children of
146 * this NodeData object. */
147 protected void setChildren(Vector v, Map idsToNodes_map)
148 {
149 // first get all child elements of type docNode or classifierNode:
150 NodeList children = nodeTag.getChildNodes();
151 if(children.getLength() == 0)
152 return; // no children, this.childnodes remains at null
153
154 int size = children.getLength();
155 for(int i = 0; i < size; i++) {
156 Node n = children.item(i);
157 // For Element-nodes, getNodeName() returns the tag name.
158 // It *never* returns null for *any* Node type
159 if(n.getNodeName().equals(GSXML.CLASS_NODE_ELEM)) {
160 Element nodeElementTag = (Element)n;
161 if(!nodeElementTag.hasAttribute(GSXML.NODE_ID_ATT))
162 continue; // should not ever be the case
163 // skip any element that does not have a nodeID Attr
164
165 String id = nodeElementTag.getAttribute(GSXML.NODE_ID_ATT);
166 if(!idsToNodes_map.containsKey(id))
167 v.add(new ClassifierNodeData(nodeElementTag));
168 else
169 v.add(idsToNodes_map.get(id));
170 } else if(n.getNodeName().equals(GSXML.DOC_NODE_ELEM)) {
171 Element nodeElementTag = (Element)n;
172 if(!nodeElementTag.hasAttribute(GSXML.NODE_ID_ATT))
173 continue; // should not ever be the case
174 // skip any element that does not have a nodeID Attr
175
176 String id = nodeElementTag.getAttribute(GSXML.NODE_ID_ATT);
177 if(!idsToNodes_map.containsKey(id))
178 v.add(new DocumentNodeData(nodeElementTag));
179 else
180 v.add(idsToNodes_map.get(id));
181 } // else: tends to be an empty textNode ("#text"), skip that
182 }
183 }
184
185 /** @return a hashmap of this document's metadata (name, val) pairs.
186 * A null is returned if the nodeMetadata has not been set yet. */
187 public Map getMetadataList() { return nodeMetadata; }
188
189
190 /** @return the title of this docNodeData object, if any is set. If it's
191 * not been set yet (title metadata not yet set, a null is returned. */
192 public String getTitle() {
193 // Each metadata (like Title) may be an array of values.
194 // Here we return the first title in the array.
195 if(this.nodeMetadata != null && this.nodeMetadata.containsKey("Title"))
196 { // we know there's a title key now
197
198 Vector v = (Vector)this.nodeMetadata.get("Title");
199 return (String)v.get(0); // return first Title element
200 }
201 else return null; // when nodeMetadata == null or no Title key
202 }
203
204 /** @return the title of this docNodeData object, if any.
205 * Otherwise the nodeID is returned.
206 * This method is useful for displaying this docNodeData object as a
207 * node in a JTree. Some meaningful displayable value is returned,
208 * unlike getTitle() which returns a null if title is not set yet. */
209 public String toString() {
210 String title = getTitle();
211 return (title == null) ? this.nodeID : title;
212 }
213
214 /** @return the details contained in this NodeData object.
215 * Useful for debugging purposes. */
216 public String show() { return this.nodeID; }
217
218 /** @return a String representation of the contains of the
219 * nodeMetadata list (useful for debugging purposes). */
220 public String showMeta() {
221 StringBuffer buf = new StringBuffer();
222 if(nodeMetadata != null && !nodeMetadata.isEmpty()) {
223 buf.append("Metadata:\n");
224 Set entrySet = nodeMetadata.entrySet();
225 Iterator i = entrySet.iterator();
226 while(i.hasNext()) {
227 Entry entry = (Entry)i.next(); // Map.Entry
228 buf.append(" name: " + (String)entry.getKey() + " values: ");
229 Vector values = (Vector)entry.getValue();
230 for(int j = 0; j < values.size(); j++)
231 buf.append((String)values.get(j) + ", ");
232 buf.append("\n");
233 }
234 }
235 return buf.toString();
236 }
237}
Note: See TracBrowser for help on using the repository browser.