KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > util > xml > applicationdata > ContextDepthDataWriter


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.util.xml.applicationdata;
11
12 import java.io.*;
13 import java.util.*;
14
15 import org.mmbase.module.core.*;
16 import org.mmbase.module.corebuilders.*;
17 import org.mmbase.util.XMLContextDepthReader;
18 import org.mmbase.util.logging.*;
19 import org.mmbase.util.xml.ApplicationReader;
20
21 /**
22  * This class is used to write (export) a selection of nodes to xml format.
23  * The nodes to export are read from a XML context file, which specifies the
24  * startnode and depth to which to parse.
25  * The current version of this class combines a number of methods which we want to split - or at least share -
26  * with a seperate class for handling contexts.
27  * Note that because of it's static nature, no object instance need be made (in fact, none CAN be made) of this class.<br />
28  *
29  * @since MMBase-1.8
30  * @author Daniel Ockeloen
31  * @author Jacco de Groot
32  * @author Pierre van Rooden
33  * @version $Id: ContextDepthDataWriter.java,v 1.2 2005/10/07 18:42:49 michiel Exp $
34  */

35 public class ContextDepthDataWriter {
36
37     /**
38      * Logging instance
39      */

40     private static Logger log = Logging.getLoggerInstance(ContextDepthDataWriter.class.getName());
41
42     /**
43      * Writes an application's nodes, according to that application's contexts, to a path.
44      * The files written are stored in a subdirectory (named after the application), and contain the datasource (xml) files for
45      * both datanodes and relation nodes.
46      * @param app A <code>ApplicationReader</code> initialised to read the application's description (xml) file
47      * This object is used to retrieve what builder and relations are needed, and in which files data should be stored.
48      * @param capp A <code>XMLContextDepthReader</code> initialised to read the application's context file
49      * This object is used to retrieve information regarding search depth and starting nodes for
50      * the search tree whoch determines what nodes are part of this application.
51      * @param targetpath The path where to save the application
52      * @param mmb Reference to the MMbase processormodule. Used to retrieve the nodes to write.
53      * @param logger Storage for messages which can be displayed to the user.
54      * @return Returns true if succesful, false if no valid depth or startnode could be found
55      * Failure of the export itself is not detected, though may be visible in the messages returned.
56      * @throws IOException if one or more files could not be written
57      */

58     public static boolean writeContext(ApplicationReader app, XMLContextDepthReader capp,String JavaDoc targetpath,
59                                        MMBase mmb, Logger logger) throws IOException {
60         // First determine the startnodes, following the specs in the current context reader.
61
int startnode=getStartNode(capp,mmb);
62         if (startnode==-1) {
63             return false;
64         }
65         // get the depth from the current context reader
66
int depth=capp.getDepth();
67         if (depth==-1) {
68             return false;
69         }
70         // get valid builders to filter
71
HashSet fb=getFilterBuilders(app.getNeededBuilders(),mmb.getTypeDef());
72
73         // the trick is to get all nodes until depth x and filter them
74
HashSet relnodes = new HashSet();
75         HashSet nodes = new HashSet();
76         getSubNodes(startnode,depth,fb, nodes,relnodes,mmb);
77
78         logger.info("Context found : "+nodes.size()+" nodes in application, "+relnodes.size()+" relations.");
79
80         // create the dir for the Data & resource files
81
File file = new File(targetpath+"/"+app.getName());
82         file.mkdirs();
83         // write DataSources
84
writeDataSources(app,nodes,targetpath,mmb,logger);
85         // write relationSources
86
writeRelationSources(app,relnodes,targetpath,mmb,logger);
87
88         return true;
89     }
90
91     /**
92      * Writes the required datasources to their corresponding xml files by calling writeNodes()
93      * @param app The ApplicationReader object, which is used to retrieve what datasources to write (and to what file).
94      * @param nodes The nodes that are part of the application. Those that are of a type compatible with the datasources are exported.
95      * @param targetpath Path where the xml files are written
96      * @param mmb MMBase object used to retrieve builder information
97      * @param logger Used to store messages that can be showmn to the user
98      */

99     static void writeDataSources(ApplicationReader app, HashSet nodes, String JavaDoc targetpath,MMBase mmb, Logger logger) {
100         writeNodes(app, nodes, targetpath, mmb, logger, false);
101    }
102
103
104     /**
105      * Writes the required relation sources to their corresponding xml files by calling writeNodes()
106      * @param app The ApplicationReader object, which is used to retrieve what relationsources to write (and to what file).
107      * @param nodes The relation nodes that are part of the application. Those that are of a type compatible with the relationsources are exported.
108      * @param targetpath Path where the xml files are written
109      * @param mmb MMBase object used to retrieve builder information
110      * @param logger Used to store messages that can be showmn to the user
111      */

112     static void writeRelationSources(ApplicationReader app, HashSet nodes, String JavaDoc targetpath,MMBase mmb, Logger logger) {
113         writeNodes(app, nodes, targetpath, mmb, logger, true);
114    }
115
116     /**
117      * Writes the nodes to their corresponding xml files
118      * @param app The ApplicationReader object, which is used to retrieve what sources to write (and to what file).
119      * @param nodes The nodes that are part of the application. Those that are of a type compatible with the sources are exported.
120      * @param targetpath Path where the xml files are written
121      * @param mmb MMBase object used to retrieve builder information
122      * @param logger Used to store messages that can be showmn to the user
123      * @param isRelation Indicates whether the nodes to write are data (false) or relation (true) nodes
124      */

125     static void writeNodes(ApplicationReader app, HashSet nodes, String JavaDoc targetpath, MMBase mmb, Logger logger,
126             boolean isRelation) {
127
128         //before we write the data first sort the list
129
//so that node fields that point to the same node type
130
//have more chance to exist. A example of this is the community
131
//where the message nodes contain a thread nodefield
132
//upon creation there first must exist a thread message
133
//so the "thread message" will have a lower number
134
List list = new Vector();
135         list.addAll(nodes);
136         Collections.sort(list, new Comparator(){
137             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
138                 return ((Integer JavaDoc)o1).compareTo((Integer JavaDoc)o2);
139             }
140         }
141         );
142         // Retrieve an enumeration of sources to write
143
// The list of sources retrieved is dependent on whether the nodes to write are data or relation nodes
144
Iterator res;
145         if (isRelation) {
146             res = app.getRelationSources().iterator();
147         } else {
148             res = app.getDataSources().iterator();
149         }
150         // determine target path subdirectory
151
String JavaDoc subtargetpath=targetpath+"/"+app.getName()+"/";
152
153         // create a list of writer objects for the nodes
154
Hashtable nodeWriters = new Hashtable();
155         while (res.hasNext()) {
156             Hashtable bset = (Hashtable)res.next(); // retrieve source builder name
157
String JavaDoc name = (String JavaDoc) bset.get("builder");
158
159             // Create nodewriter for this builder
160
NodeWriter nw = new NodeWriter(mmb, logger, subtargetpath, name, isRelation);
161             // and store in table
162
nodeWriters.put(name, nw);
163         }
164         MMObjectBuilder bul = mmb.getMMObject("typedef"); // get Typedef object
165
int nrofnodes=0; // set total nodes to export to zero (is this used?).
166
// Store all the nodes that apply using their corresponding NodeWriter object
167
for (Iterator nods=list.iterator(); nods.hasNext(); ) {
168         // retrieve the node to export
169
int nr = ((Integer JavaDoc)nods.next()).intValue();
170             MMObjectNode node = bul.getNode(nr);
171             String JavaDoc name = node.getName();
172             NodeWriter nodeWriter = (NodeWriter)nodeWriters.get(name);
173             // export the node if the writer was found
174
if (nodeWriter!=null) {
175                 nodeWriter.write(node);
176                 nrofnodes++;
177             }
178             // if null, the node was specified as being part of the application, but should not (for some reason) be exported
179
// note that this plays havoc with the relations!
180
// better solution (not implemented): create Writers 'on the fly' if necessary, and export
181
// everything, even if no datasource is given (should not be too tough), but this also means changing the context file.
182
}
183
184         // close the files.
185
for (Enumeration e = nodeWriters.keys(); e.hasMoreElements();) {
186             String JavaDoc name = (String JavaDoc)e.nextElement();
187             NodeWriter nodeWriter;
188             nodeWriter = (NodeWriter)nodeWriters.get(name);
189             nodeWriter.done();
190         }
191     }
192
193     /**
194     * Determines the number of the node referenced by another node.
195     * @param nodeNumber number of the referencing node
196     * @param relationNode node from the relationtable containing the relation data
197     * @returns An <code>int</code> value for the number of the node referenced
198     */

199     static int getRelatedNode(int nodeNumber, MMObjectNode relationNode) {
200         int snumber = relationNode.getIntValue("snumber"); // referenced node is either source
201
if (snumber == nodeNumber) {
202             return relationNode.getIntValue("dnumber"); // or destination
203
} else {
204             return snumber;
205         }
206     }
207
208     /**
209      * Searches the MMBase cloud, colelcting all nodes (and corresponmding relation nodes) that belong to a specific
210      * type, and which can be traced up to a certain depth of nodes to a starting node.
211      *
212      * @param startnodenr the number of the node to start with
213      * @param maxdeoth the maximum depth a tree is traversed. A depth of 0 or less means only the sdtartnode is added.
214      * A depth of one includes all teh nodes refernced by the startnode, etc.
215      * Relation nodes are not counted when determining 'depth'.
216      * @param fb a <code>HashSet</code> containing the set of types that are allowed for export
217      * @param nodesdoneSet A <code>HashSet</code> which holds all nodes that are already 'done' or 'almost done'. this set is expanded in the method
218      * nodes already in this set are skipped (optimization). After return, the set has been expanded
219      * with all nodes found while traversing the cloud
220      * @param mmb MMBase object used to retrieve builder information
221      */

222
223     static void getSubNodes(int startnodenr, int maxdepth, HashSet fb, HashSet nodesdoneSet, HashSet relationnodesSet,MMBase mmb) {
224         HashSet nodesSet_current = null; // holds all nodes not yet 'done' that are on the current level
225
HashSet nodesSet_next = new HashSet(); // holds all nodes not yet 'done' that are on the next level
226
InsRel bul = mmb.getInsRel(); // builder for collecting relations. should be changed to MMRelations later on!
227
Integer JavaDoc type = new Integer JavaDoc(bul.getNodeType(startnodenr)); // retrieve node type (new method in MMObjectBuiilder)
228
if (!fb.contains(type)) { // exit if the type of this node conflicts.
229
// essentially, no nodes are added. This can only occur if the context of
230
// an application specified an invalid node.
231
return;
232         }
233         nodesSet_next.add(new Integer JavaDoc(startnodenr)); // add the very first node to the set...
234
// For each depth of the tree, traverse the nodes on that depth
235
for (int curdepth=1;curdepth<=maxdepth;curdepth++) {
236             nodesSet_current = nodesSet_next; // use the next level of nodes to tarverse
237
nodesSet_next = new HashSet(); // and create a new holder for the nodes one level deeper
238

239             // since the nodes on this level are 'almost done', and therefor should be skipped
240
// when referenced in the next layer, add the current set to the set of nodes that are 'done'
241
//
242
nodesdoneSet.addAll(nodesSet_current);
243             // iterate through the current level
244
for (Iterator curlist=nodesSet_current.iterator(); curlist.hasNext();) {
245                 // get the next node's number
246
Integer JavaDoc thisnodenr = (Integer JavaDoc)curlist.next();
247                 // Iterate through all the relations of a node
248
// determining relations has to be adapted when using MMRelations!
249
for (Iterator rel=bul.getRelationsVector(thisnodenr.intValue()).iterator(); rel.hasNext();) {
250                     // get the relation node and node number
251
MMObjectNode relnode=(MMObjectNode)rel.next();
252                     Integer JavaDoc relnumber=new Integer JavaDoc(relnode.getIntValue("number"));
253                     // check whether to add the referenced node
254
// and the relation between this node and the referenced one.
255
// if relation is in pool, save trouble and do not traverse further
256
if (!relationnodesSet.contains(relnumber)) {
257                         // determine node referenced
258
int nodenumber=getRelatedNode(thisnodenr.intValue(),relnode);
259                         // check type of referenced node
260
type = new Integer JavaDoc(bul.getNodeType(nodenumber));
261                         if (fb.contains(type)) { // good node? then proceed
262
// add the relation node
263
relationnodesSet.add(relnumber);
264                             // if the node has been 'done', don't add it!
265
Integer JavaDoc nodeNumber=new Integer JavaDoc(nodenumber);
266                             if (!nodesdoneSet.contains(nodeNumber)) {
267                                 // because we use a set, no double nodes will be added (cool, uh?)
268
nodesSet_next.add(nodeNumber);
269                             }
270                         }
271                     }
272                 }
273             }
274         }
275         // add the last retrieved set to the set of nodes that are 'done'
276
nodesdoneSet.addAll(nodesSet_next);
277         return;
278     }
279
280     /**
281      * Retrieves the builders used for filtering the nodes for this application
282      * @param filter Vector containign all the buildernames that are part of this application
283      * Note that being part of an application does not mean that they are exported!
284      * @param bul reference to the TypeDef builder, used for rertrieving builder types
285      * @return a <code>HashSet</code>, containing the types (Integer) of all builders part of this application.
286      */

287     static HashSet getFilterBuilders(Vector filter,TypeDef bul) {
288         HashSet resultset=new HashSet();
289         for(Iterator res=filter.iterator(); res.hasNext(); ) {
290             Hashtable bset=(Hashtable)res.next();
291             String JavaDoc name=(String JavaDoc)bset.get("name");
292             int value=bul.getIntValue(name);
293             if (value!=-1) {
294                 resultset.add(new Integer JavaDoc(value));
295             } else {
296                 log.error("XMLContextDepthWriter -> can't get intvalue for : "+name);
297             }
298         }
299         return resultset;
300     }
301
302
303     /**
304      * Retrieves the number of the startnode referenced by the context configuration file..
305      * Returns always only one node (should be changed?)
306      * @param capp XMLContextDepthReader object for retrieving data from the context
307      * @param mmb reference to the MMBase object, used for retrieving aliases and builders
308      * @return An <code>integer</code>, the number of the startnode if succesful, -1 otherwise.
309      */

310     static int getStartNode(XMLContextDepthReader capp, MMBase mmb) {
311         // first check for an alias
312
String JavaDoc alias=capp.getStartAlias();
313         if (alias!=null) {
314             // if so, get the node associated with that alias
315
OAlias bul=(OAlias)mmb.getMMObject("oalias");
316             int number=bul.getNumber(alias);
317             if (number==-1) log.error("Invalid Start Node Alias please make sure its valid");
318             return number;
319         } else {
320             // otherwise, get a builder and the where clause to run on that builder
321
String JavaDoc builder=capp.getStartBuilder();
322             String JavaDoc where=capp.getStartWhere();
323             // retrieve the actual builder
324
MMObjectBuilder bul=mmb.getMMObject(builder);
325             if (bul!=null) {
326                 // find the nodes that match
327
Enumeration results=bul.search(where);
328                 // check if there are any nodes
329
if (results.hasMoreElements()) {
330                     // then return the first node found.
331
MMObjectNode node=(MMObjectNode)results.nextElement();
332                     return node.getIntValue("number");
333                 }
334             } else {
335                 log.error("ContextDepthWriter-> can't find builder ("+builder+")");
336             }
337         }
338         log.error("Invalid Start Node please fix your 'where' settings or use a alias");
339         return -1;
340     }
341
342     /**
343      * Saves a string value to a file.
344      * @param filename Name of the file to save.
345      * @param value string to store in the file
346      * @return True if succesfull, false if an error occurred.
347      */

348     static boolean saveFile(String JavaDoc filename,String JavaDoc value) {
349         File sfile = new File(filename);
350         try {
351             DataOutputStream scan = new DataOutputStream(new FileOutputStream(sfile));
352             scan.writeBytes(value);
353             scan.flush();
354             scan.close();
355         } catch(Exception JavaDoc e) {
356             log.error(e);
357             log.error(Logging.stackTrace(e));
358             return false;
359         }
360         return true;
361     }
362
363     /**
364      * Saves an array of byte to a file.
365      * @param filename Name of the file to save.
366      * @param value array to stiore in the file
367      * @return True if succesfull, false if an error occurred.
368      */

369     static boolean saveFile(String JavaDoc filename,byte[] value) {
370         File sfile = new File(filename);
371         try {
372             DataOutputStream scan = new DataOutputStream(new FileOutputStream(sfile));
373             scan.write(value);
374             scan.flush();
375             scan.close();
376         } catch(Exception JavaDoc e) {
377             log.error(e);
378             log.error(Logging.stackTrace(e));
379             return false;
380         }
381         return true;
382     }
383
384     /**
385      * Writes the context file, based on what was supplied by the application
386      * @param capp XMLContextDepthReader providing original context data
387      * @param filename Name of the xml file to save.
388      * @return always true
389      */

390     public static boolean writeContextXML(XMLContextDepthReader capp,String JavaDoc filename) {
391         String JavaDoc body="<contextdepth>\n";
392         String JavaDoc alias=capp.getStartAlias();
393         if (alias!=null) {
394             body+="\t<startnode alias=\""+alias+"\" />\n";
395         } else {
396             body+="\t<startnode>\n";
397             body+="\t\t<builder>"+capp.getStartBuilder()+"</builder>\n";
398             body+="\t\t<where>"+capp.getStartWhere()+"</where>\n";
399             body+="\t</startnode>\n\n";
400         }
401         body+="\t<depth>"+capp.getDepth()+"</depth>\n";
402         body+="</contextdepth>\n";
403         saveFile(filename,body);
404         return true;
405     }
406
407 }
408
Popular Tags