[jdom-interest] Has any one done a JDOM <> JTreeModel adaptor
Frank Sauer
Frank.Sauer at trcinc.com
Thu May 24 08:00:45 PDT 2001
Very interesting. You use a class called Schema.
what package is that from, or did you write it?
If the latter, could you post it?
Thanks,
Frank
-----Original Message-----
From: Kenworthy, Edward [mailto:edward.kenworthy at exchange.co.uk]
Sent: Thursday, May 24, 2001 10:12 AM
To: 'adam flinton'; Kenworthy, Edward; 'Matthew MacKenzie'; Richard Cook
Cc: JDOM-Interest
Subject: RE: [jdom-interest] Has any one done a JDOM <> JTreeModel
adaptor
Sure, this is XMLDocument, which I use to wrap the JDOM Document as
described (I use log4j for logging but you could just omit that code).
package reqxml;
import java.util.Collection;
import java.util.List;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Vector;
import java.text.MessageFormat;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.File;
import javax.swing.event.EventListenerList;
import org.jdom.*;
import org.jdom.output.XMLOutputter;
import org.jdom.input.SAXBuilder;
import org.apache.log4j.Category;
/**
* Extends the JDOM document type to support the observer-observable
paradigm using Listeners.
* Note that the Element modification methods on this class should be used
rather
* than directly modifying the Element itself.
* Unlike a normal XML document an element CAN have more than one parent.
What connects the XML element
* is the value of the "name" attribute. If there is no name attribute then
each element is treated as unique.
* If the element type (name) and value of the name attribute are the same,
ignoring case, then the element is treated
* as being the same element.
*
* Important note, XMLDocument objects can be re-used.
*
* 27/4/01 Some changes made to wrap Element inside Requirement. Changes can
now be made to Requirement and will
* be reflected in the document.
*
* @author Edward Kenworthy
* @version 1.0
*/
public class XMLDocument
{
// XML Element attribute name
public final static String FILENAME_ATT = "filename";
public final static String NAME_ATT="name";
private final static String SCHEMA_ATT="schema";
// Resource keys
// errors
private final static String NEW_REQ_PRJ_TITLE="NewReqPrjTitle";
private final static String
BAD_SCHEMA_BAD_PROJ_TYPE="BadSchemaBadPrjType";
private final static String
CHILD_CANT_BE_OWN_PARENT_FMT="ChildCantBeOwnParentFmt";
private final static String
CHILD_REQ_TYPE_NOT_ALLOWED="ChildReqTypeNotAllowed";
private final static String
NOT_ALLOWED_DETACH_ROOT="NotAllowedToDetachRootReq";
// info
private final static String
INFO_LOADING_REQ_FILE_FMT="InfoLoadingReqFileFmt";
private final static String
INFO_LOADED_REQ_FILE_FMT="InfoLoadedReqFileFmt";
private final static String
INFO_LOADING_SCHEMA_FILE_FMT="InfoLoadingSchemaFileFmt";
private final static String
INFO_LOADED_SCHEMA_FILE_FMT="InfoLoadedSchemaFileFmt";
private final static String INFO_NO_SCHEMA="InfoNoSchema";
private final static String FREE_FORM_PROJECT_NAME =
"ReqXMLFreeFormatProject";
// Logging
private static final Category logger =
Category.getInstance(XMLDocument.class.getName());
private Document _doc = null;
private File _file = null;
private Schema _schema = null;
public XMLDocument()
{
_doc = new Document(new Element(FREE_FORM_PROJECT_NAME));
}
private final static SAXBuilder _builder = new SAXBuilder();
public XMLDocument(File file) throws JDOMException
{
internalLoad(file);
}
public Schema getSchema()
{
return _schema;
}
public void load(File file) throws JDOMException
{
internalLoad(file);
setDirty(false);
fireDocumentChangedEvent(new DocChangedEvent(this,
DocChangedEvent.DOC_LOADED));
}
private void internalLoad(File file) throws JDOMException
{
_file = file;
logger.info(MessageFormat.format(ReqXML.getStringProperty(INFO_LOADING_REQ_F
ILE_FMT), new Object[]{_file}));
_doc = _builder.build(_file);
logger.info(MessageFormat.format(ReqXML.getStringProperty(INFO_LOADED_REQ_FI
LE_FMT), new Object[]{_file}));
Element root = _doc.getRootElement();
Attribute schemaAtt = root.getAttribute(SCHEMA_ATT);
if (null != schemaAtt)
{
String schemaFileName = schemaAtt.getValue();
if (schemaFileName.length() > 0)
{
logger.info(MessageFormat.format(ReqXML.getStringProperty(INFO_LOADING_SCHEM
A_FILE_FMT), new Object[]{schemaFileName}));
_schema = new Schema(schemaFileName);
logger.info(MessageFormat.format(ReqXML.getStringProperty(INFO_LOADED_SCHEMA
_FILE_FMT), new Object[]{schemaFileName}));
}
else
{
_schema = null;
}
}
if (null == _schema)
{
logger.info(ReqXML.getStringProperty(INFO_NO_SCHEMA));
}
}
public File getFile()
{
return _file;
}
public String getTitle()
{
/** @todo perhaps also include the schema in the title */
if (null != _file)
{
return _file.getAbsolutePath();
}
return ReqXML.getStringProperty(NEW_REQ_PRJ_TITLE);
}
public boolean isReadOnly()
{
if (null != _file)
{
return !_file.canWrite();
}
return false;
}
public void newProject(Schema schema)
{
if (null != schema)
{
logger.debug("Creating a new, empty, project based on a schema.");
Schema oldSchema = _schema;
File oldFile = _file;
_file = null;
_schema = schema;
Document oldDoc = _doc; // so we can restore if things go wrong
try
{
Element rootElement = new Element(schema.getProjectRootType());
rootElement.addAttribute(new Attribute(SCHEMA_ATT,
_schema.getFileName()));
_doc = new Document(rootElement);
// Notify the update
Collection mandatoryRootChildren =
schema.generateMandatoryRootChildren(this);
Requirement rootRequirement = getRootRequirement();
Iterator iter = mandatoryRootChildren.iterator();
while(iter.hasNext())
{
Requirement req = ((Requirement)iter.next());
logger.debug("Adding mandatory child " + req.getName() + " to the
root.");
rootRequirement.addChild(req);
}
setDirty(false);
fireDocumentChangedEvent(new DocChangedEvent(this,
DocChangedEvent.DOC_LOADED));
logger.debug("**Succesfully finished creating a new, empty, project
based on a schema.");
}
catch (IllegalNameException ine)
{
logger.error(ReqXML.getStringProperty(BAD_SCHEMA_BAD_PROJ_TYPE));
// no need to throw an exception as the document is left "as is" ie
the current project remains undisturbed.
_doc = oldDoc;
_file = oldFile;
_schema = oldSchema;
// logger.debug("**Failed to create a new, empty, project based on a
schema.");
}
catch (Schema.SchemaException se)
{
logger.error(se.getMessage());
_doc = oldDoc;
_file = oldFile;
_schema = oldSchema;
// logger.debug("**Failed to create a new, empty, project based on a
schema.");
}
}
else // free format project
{
logger.debug("Creating a new empty free-form project.");
_doc = new Document(new Element(FREE_FORM_PROJECT_NAME));
_schema = null;
// Notify the update
setDirty(false);
fireDocumentChangedEvent(new DocChangedEvent(this,
DocChangedEvent.DOC_LOADED));
}
}
// Getters
public DocType getDocType()
{
return _doc.getDocType();
}
public Requirement getRootRequirement()
{
return new Requirement(this, _doc.getRootElement());
}
/*package*/ void addChildElement(Element parent, Element newChild)
{
// Check parent != child
if (parent.getName().equalsIgnoreCase(newChild.getName()))
{
Attribute parentNameAtt = parent.getAttribute(NAME_ATT);
if (parentNameAtt != null)
{
String parentName = parentNameAtt.getValue();
if (null != parentName && parentName.length() != 0)
{
Attribute childNameAtt = newChild.getAttribute(NAME_ATT);
if (childNameAtt != null)
{
String childName = childNameAtt.getValue();
if (null != childName && childName.length() != 0)
{
if (childName.equalsIgnoreCase(parentName))
{
// uhoh
String
message=MessageFormat.format(ReqXML.getStringProperty(CHILD_CANT_BE_OWN_PARE
NT_FMT), new Object[]{childName});
logger.warn(message);
throw new RuntimeException(message);
}
}
}
}
}
}
// Do the job
try
{
Collection allInstances = getAllInstances(parent);
logger.debug("There are " + allInstances.size() + " of the parent, " +
parent.getName());
Iterator iter = allInstances.iterator();
while(iter.hasNext())
{
Element currentMatch = (Element)iter.next();
internalAddChildElement(currentMatch, newChild);
}
}
catch (Schema.SchemaException se)
{
logger.error(se.getMessage());
}
// Notify the update
setDirty();
fireDocumentChangedEvent(new DocChangedEvent(this));
}
/*package*/ boolean isElementAddable(String candidateChildType, Element
parent)
{
if (null != _schema)
{
try
{
return _schema.isElementAddable(candidateChildType, parent);
}
catch (Schema.SchemaException se)
{
logger.error(se.getMessage());
return false;
}
}
return true; // if there is no schema then anything goes.
}
/**
* If it returns null then that means ANY element can be child of this
parent
*/
/*package*/ Collection getAllValidChildTypes(Element parent) throws
Schema.SchemaException
{
if (null != _schema)
{
return _schema.getAllValidChildTypes(parent);
}
return null;
}
/**
* Note that add a child will remove it's old parent. (Behaviour of
Element).
*/
private void internalAddChildElement(Element parent, Element newChild)
throws Schema.SchemaException
{
logger.debug("entered internalAddChildElement");
if (_schema.isElementAddable(newChild.getName(), parent))
{
parent.addContent(newChild);
}
else
{
String message =
MessageFormat.format(ReqXML.getStringProperty(CHILD_REQ_TYPE_NOT_ALLOWED),
new Object[]{newChild.getName(), parent.getName()});
logger.error(message);
throw new RuntimeException(message);
}
}
/**
* This method returns all child elements with the given name attribute and
element type.
* In our model one element can appear multiple times. However in XML this
ain't supported.
* So how do we know it's the same element ? Because the type and name
match.
*/
private Collection getAllInstances(Element element)
{
// check if there are multiple instances of the element
Vector listOfInstances = new Vector();
Attribute elementNameAtt = element.getAttribute(NAME_ATT);
if (elementNameAtt != null)
{
String elementName = elementNameAtt.getValue();
if (elementName.length() != 0)
{
return internalGetAllInstances(_doc.getRootElement(), element,
listOfInstances);
}
}
logger.debug("Element doesn't have a name attribute set, so can't have
more than one instance.");
listOfInstances.add(element);
return listOfInstances;
}
/**
* Recursive helper method.
*/
/** @todo use a loop rather than recursion */
private Collection internalGetAllInstances(final Element searchRoot, final
Element element, final Collection listOfMatches)
{
// logger.debug("internalGetAllInstances(searchRoot = " +
searchRoot.getName() + ", element = " + element.getName() + ",
listOfMatches)");
// check if there are multiple instances of the element
Attribute elementNameAtt = element.getAttribute(NAME_ATT);
if (elementNameAtt != null)
{
String elementName = elementNameAtt.getValue();
if (elementName.length() != 0)
{
List children = searchRoot.getChildren();
if (children.size() > 0)
{
Iterator iter = children.iterator();
while(iter.hasNext())
{
Element current = (Element)iter.next();
if (current.getName().equalsIgnoreCase(element.getName()))
{
Attribute nameAtt = current.getAttribute(NAME_ATT);
if (nameAtt != null)
{
String name = nameAtt.getValue();
if (name.length() != 0)
{
if (name.equalsIgnoreCase(elementName))
{
// logger.debug("Everything matches, add it to the list.");
listOfMatches.add(current);
}
else
{
// logger.debug("Types match, but name doesn't, so check
its children.");
internalGetAllInstances(current, element,
listOfMatches);
}
}
else
{
// logger.debug("Types match, but name attribute not set to
anything. Check its children.");
internalGetAllInstances(current, element, listOfMatches);
}
}
else
{
// logger.debug("Types match, but there's no name attribute.
Check its children.");
internalGetAllInstances(current, element, listOfMatches);
}
}
else
{
// logger.debug("Types don't match, can't be the same, so check
its children.");
internalGetAllInstances(current, element, listOfMatches);
}
} // while
}
else
{
// logger.debug("searchRoot has no children.");
}
}
else
{
// logger.debug("No name attribute set, can't have other instances");
}
}
else
{
// logger.debug("No name attribute at all, can't have other
instances");
}
return listOfMatches;
}
/*package*/ Collection getParentsOfElement(Element element) // there may
be no parents
{
logger.debug("Getting parents for element " + element.getName());
Collection parents = new Vector();
Element parent = element.getParent();
Attribute nameAtt = element.getAttribute(NAME_ATT);
if (null != nameAtt)
{
String name = nameAtt.getValue();
if (null != name && name.length() != 0)
{
Collection instancesOfElement = getAllInstances(element);
// logger.debug("Found " + instancesOfElement.size() + " instances of
this element.");
Iterator iter = instancesOfElement.iterator();
while (iter.hasNext())
{
Element instanceParent = ((Element)iter.next()).getParent();
if (null != instanceParent)
{
// logger.debug("Adding a parent.");
parents.add(instanceParent);
}
}
}
else if (parent != null) // can only be one parent
{
// logger.debug("No value set for name attribute, can only be one
parent.");
parents.add(parent);
}
}
else if (parent != null) // can only have one parent
{
// logger.debug("No name attribute, can only be one parent.");
parents.add(parent);
}
logger.debug("Returning a list of " + parents.size() + " parents.");
return parents;
}
/*package*/ void detachChildElement(Element childElement)
{
Element parent = childElement.getParent();
if (null != parent)
{
java.util.List siblings = parent.getChildren();
siblings.remove(childElement);
// Mark it is changed
setDirty();
fireDocumentChangedEvent(new DocChangedEvent(this));
}
else
{
throw new
RuntimeException(ReqXML.getStringProperty(NOT_ALLOWED_DETACH_ROOT));
}
}
//////////////////////////////////////
//
private boolean _dirty = false;
/**
* Checks whether the requirements set needs saving.
* @returns true if the requirements hierarchy has been modified since it
was last saved.
*/
public boolean isDirty()
{
return _dirty;
}
/*package*/ void setDirty(boolean dirty)
{
_dirty = dirty;
}
public void setDirty()
{
_dirty = true;
}
// XMLDocumentException class
class XMLDocumentException extends Exception
{
public XMLDocumentException()
{
}
public XMLDocumentException(String message)
{
super(message);
}
}
// Save / Load
public void save() throws IOException, XMLDocumentException
{
if (_file != null)
{
String fileName = _file.getAbsolutePath();
_file.renameTo(new File(fileName + ".bak"));
File saveFile = new File(fileName);
// Output the document, use standard formatter
XMLOutputter fmt = new XMLOutputter();
fmt.output(_doc, new FileOutputStream(saveFile));
_file = saveFile;
setDirty(false);
fireDocumentChangedEvent(new DocChangedEvent(this,
DocChangedEvent.DOC_SAVED));
}
else
{
/** @todo text to resource file*/
throw new XMLDocumentException("No file name for XMLDocument - save
aborted.");
}
}
public void saveAs(File file) throws IOException, XMLDocumentException
{
File oldFile = _file;
_file = file;
try
{
save();
}
catch (IOException ioe)
{
_file = oldFile;
throw ioe;
}
}
//////////////////////////////////////
// Listener management and Event dispatching
protected final Vector eventListeners = new Vector();
public void addListener(XMLDocumentListener listener)
{
eventListeners.add(listener);
}
public void removeListener(XMLDocumentListener listener)
{
eventListeners.remove(listener);
}
protected void fireDocumentChangedEvent(DocChangedEvent evt)
{
logger.debug("Document changed " + evt);
for (int i = 0; i < eventListeners.size(); i++)
{
logger.debug("Firing a doc changed event to " +
eventListeners.elementAt(i).getClass().getName());
((XMLDocumentListener)eventListeners.elementAt(i)).eventFired(evt);
}
}
}
#
#
#
# This next class is my TreeModel
#
#
#
package reqxml;
import java.util.Iterator;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Vector;
import java.io.IOException;
import java.io.File;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.DefaultMutableTreeNode;
import org.apache.log4j.Category;
/**
* This acts as a wrapper for an XMLDocument object and keeps the tree model
* and document in synch.
*
* @author Edward Kenworthy
* @version 1.0
*/
public class XMLDocTreeModel extends DefaultTreeModel implements
XMLDocumentListener
{
// Logging
private static final Category logger =
Category.getInstance(XMLDocTreeModel.class.getName());
private final XMLDocument _doc;
public XMLDocTreeModel(XMLDocument doc)
{
super(new DefaultMutableTreeNode(doc.getRootRequirement()));
// logger.debug("Root element is " + doc.getRootElement().getName());
_doc=doc;
BuildModel((DefaultMutableTreeNode)getRoot());
_doc.setDirty(false);
_doc.addListener(this);
}
public XMLDocument getDocument()
{
return _doc;
}
/**
* Handles parent-child relationships
*/
private void BuildModel(DefaultMutableTreeNode parent)
{
if (((Requirement)(parent.getUserObject())).hasChildren())
{
Collection children =
((Requirement)(parent.getUserObject())).getChildren();
Iterator iter = children.iterator();
while (iter.hasNext())
{
Requirement child = (Requirement)iter.next();
BuildModel(AddRequirementToNode(parent, child));
}
}
}
/* internal method */
private DefaultMutableTreeNode AddRequirementToNode(DefaultMutableTreeNode
parent, Requirement childRequirement)
{
DefaultMutableTreeNode child = new
DefaultMutableTreeNode(childRequirement);
super.insertNodeInto(child, parent, parent.getChildCount());
return child;
}
/*package*/ DefaultMutableTreeNode
InsertRequirementIntoNode(DefaultMutableTreeNode parent, Requirement
childRequirement)
{
((Requirement)parent.getUserObject()).addChild(childRequirement);
DefaultMutableTreeNode newNode = AddRequirementToNode(parent,
childRequirement);
BuildModel(newNode);
return newNode;
}
// Model manipulation
public void setRoot(TreeNode root)
{
logger.error("XMLDocTreeModel::setRoot() Unimplemented method called.");
throw new RuntimeException("XMLDocTreeModel::setRoot() Unimplemented
method called.");
// super.setRoot(root);
// update document
/** @todo */
}
public void insertNodeInto(DefaultMutableTreeNode newChild,
DefaultMutableTreeNode parent, int index)
{
logger.error("XMLDocTreeModel::insertNodeInto() Unimplemented method
called.");
throw new RuntimeException("XMLDocTreeModel::insertNodeInto()
Unimplemented method called.");
// super.insertNodeInto(newChild, parent, index);
// update document
/** @todo */
}
public void removeNodeFromParent(DefaultMutableTreeNode node)
{
super.removeNodeFromParent(node);
// update document
/** @todo */
logger.debug("Danger Will Robinson. line 100(ish)");
((Requirement)node.getUserObject()).removeFromParent();
}
////////////////////////////////////////////////////////////
// Doc event handling (XMLDocumentListener)
public void eventFired(DocChangedEvent evt)
{
if (DocChangedEvent.REQUIREMENT_MODIFIED == evt.getChangeType())
{
/**
* @todo need to do this properly. Should do something like:
* - find the node corresponding to the element that has been changed
* - fire a node changed event for that node (remember element can
relate to many nodes.)
* =OR=
* - just getting the treeview to re-paint itself would also work.
*/
logger.debug("(Unfinished)XMLDocTreeModel handled an XMLDocument
REQUIREMENT_MODIFIED event, but it's a kludge.");
// reload();
}
else if (DocChangedEvent.DOC_LOADED == evt.getChangeType())
{
logger.debug("(Unfinished)XMLDocTreeModel handled an XMLDocument
DOC_LOADED event, but it's a kludge.");
super.setRoot(new DefaultMutableTreeNode(_doc.getRootRequirement()));
BuildModel((DefaultMutableTreeNode)getRoot());
reload();
fireDocumentChangedEvent(new
ReqTreeModelEvent(ReqTreeModelEvent.MODEL_LOADED));
}
else if (DocChangedEvent.NONSPECIFIC_CHANGE == evt.getChangeType())
{
/**
* @todo need to do this properly. Something like just getting the
treeview to re-paint
* would do it.
* 18/5/01 Or maybe not as HierarchyEditor is a document listener as
well.
*/
logger.debug("(Unfinished)XMLDocTreeModel handled an XMLDocument
NONSPECIFIC_CHANGE event, but it's a kludge.");
// reload();
}
}
//////////////////////////////////////
// Listener management and Event dispatching
protected final Vector eventListeners = new Vector();
public void addListener(ReqTreeModelListener listener)
{
eventListeners.add(listener);
}
public void removeListener(ReqTreeModelListener listener)
{
eventListeners.remove(listener);
}
protected void fireDocumentChangedEvent(ReqTreeModelEvent evt)
{
logger.debug("ReqTreeModel changed " + evt);
for (int i = 0; i < eventListeners.size(); i++)
{
logger.debug("Firing a ReqTreeModel changed event to " +
eventListeners.elementAt(i).getClass().getName());
((ReqTreeModelListener)eventListeners.elementAt(i)).eventFired(evt);
}
}
}
#
#
#
# This is my Schema class - note that I don't need full
# XML schema support for my app.
#
#
#
package reqxml;
import java.util.Map;
import java.util.Collection;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import java.text.MessageFormat;
import java.io.File;
import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.apache.log4j.Category;
/**
* Description:
* @author Edward Kenworthy
* @version 1.0
*/
class Schema
{
// property keys for messages
private final static String BAD_SCHEMA_BAD_ROOT="BadSchemaRootNoName";
private final static String
BAD_SCHEMA_BAD_REQ_TYPE_FMT="BadSchemReqTypeFmt";
private final static String
BAD_SCHEMA_NO_NAME_ATT_FMT="BadSchemaNoNameAttFmt";
private final static String
BAD_SCHEMA_BAD_MINOCCURS_FMT="BadSchemaBadMinOccursFmt";
private final static String
INVALID_REQUIREMENT_TYPE="InvalidRequirementType";
private final static String
BAD_SCHEMA_ELEMENT_LACKS_NAME_REF="ElementLacksnameAndRef";
// XML Schema magic strings
public final static String XML_SCHEMA_ATT_NAME = "name"; // attribute of
element "attribute" that defines the name of the attribute
public final static String XML_SCHEMA_COMPLEXTYPE = "complexType";
public final static String XML_SCHEMA_ELEMNAME = "name";
public final static String XML_SCHEMA_MINOCCURS = "minOccurs";
public final static String XML_SCHEMA_SIMPLETYPE = "element";
public final static String XML_SCHEMA_ELEMREF = "ref";
public final static String XML_SCHEMA_ATTRIB = "attribute";
// Logging
private static final Category logger =
Category.getInstance(Schema.class.getName());
private final static SAXBuilder _builder = new SAXBuilder();
private Document _schema;
private String _fileName;
public Schema(String fileName) throws JDOMException
{
_fileName = fileName;
_schema = _builder.build(_fileName);
}
public String getFileName()
{
return _fileName;
}
public String getDirectory()
{
return (new File(_fileName)).getParentFile().getAbsolutePath();
}
/** @todo need to take account of maxOccurs ? Might be too much of a pain
for the user though. */
/*package*/ boolean isElementAddable(String candidateChildType, Element
parent) throws SchemaException
{
Collection validChildTypes = getAllValidChildTypes(parent);
boolean result = false;
Iterator iter = validChildTypes.iterator();
while (iter.hasNext())
{
if (((String)iter.next()).equals(candidateChildType))
{
result = true;
break;
}
}
// logger.debug("isElementAddable() he say " + (result ? "Yes!" :
"No."));
return result;
}
public String getProjectRootType() throws SchemaException
{
Element schemaElement = _schema.getRootElement();
Element rootElement = schemaElement.getChild(XML_SCHEMA_COMPLEXTYPE);
Attribute projectRootType =
rootElement.getAttribute(XML_SCHEMA_ELEMNAME);
if (null != projectRootType)
{
return projectRootType.getValue();
}
throw new
SchemaException(ReqXML.getStringProperty(BAD_SCHEMA_BAD_ROOT));
}
public Collection generateMandatoryRootChildren(XMLDocument doc) throws
SchemaException
{
Collection mandatoryRootChildren = new Vector();
Element schemaElement = _schema.getRootElement();
Element rootElement = schemaElement.getChild(XML_SCHEMA_COMPLEXTYPE); //
note the name
List children = rootElement.getChildren();
logger.debug("Schema says there are " + children.size() + " child
elements of root " + rootElement.getName() + ", sorting out which are
mandatory.");
Iterator iter = children.iterator();
while(iter.hasNext())
{
Element currentElement = (Element)iter.next();
Attribute minOccursAtt =
currentElement.getAttribute(XML_SCHEMA_MINOCCURS);
if (null != minOccursAtt)
{
try
{
int minOccurs = minOccursAtt.getIntValue();
Attribute requirementTypeAtt =
currentElement.getAttribute(XML_SCHEMA_ELEMNAME);
if (null != requirementTypeAtt)
{
String requirementType = requirementTypeAtt.getValue();
try
{
for (int i = 0; i < minOccurs; i++)
{
/** @todo handle attributes and mandatory children of these */
logger.debug("Adding a " + requirementType + " to the list
of mandatory root elements");
mandatoryRootChildren.add(new Requirement(doc,
requirementType));
}
}
catch (IllegalNameException ine)
{
throw new
SchemaException(MessageFormat.format(ReqXML.getStringProperty(BAD_SCHEMA_BAD
_REQ_TYPE_FMT), new Object[]{requirementType}));
}
}
else
{
throw new
SchemaException(MessageFormat.format(ReqXML.getStringProperty(BAD_SCHEMA_NO_
NAME_ATT_FMT), new Object[]{currentElement.getName()}));
}
}
catch (DataConversionException dce)
{
throw new
SchemaException(MessageFormat.format(ReqXML.getStringProperty(BAD_SCHEMA_BAD
_MINOCCURS_FMT), new Object[]{currentElement.getName(),
minOccursAtt.getValue()}));
}
}
} // while
logger.debug("Schema says there are " + mandatoryRootChildren.size() + "
mandatory root children.");
return mandatoryRootChildren;
}
private final Map _cacheOfValidChildTypes = new HashMap(); // cache of
results from getAllValidChildTypes()
/**
* If it returns null then that means ANY element can be child of this
parent
*/
/*package*/ Collection getAllValidChildTypes(Element parent) throws
SchemaException
{
logger.debug("Checking allowed children for " + parent.getName());
// if (null != _schema)
// {
// try and find it in the cache first
List allowedChildren =
(List)_cacheOfValidChildTypes.get(parent.getName());
if (null == allowedChildren)
{
logger.debug("Not found in the cache, looking it up.");
allowedChildren = new Vector();
Element parentInSchema = findRequirementInSchema(parent.getName());
if (null != parentInSchema)
{
List allChildren = parentInSchema.getChildren();
Iterator iter = allChildren.iterator();
// the structure used in the following code is straight from the XML schema
spec.
while(iter.hasNext())
{
Element current = (Element)iter.next();
if ((current.getName().equals(XML_SCHEMA_SIMPLETYPE)) ||
(current.getName().equals(XML_SCHEMA_COMPLEXTYPE)))
{
String elementType;
Attribute nameAtt = current.getAttribute(XML_SCHEMA_ELEMNAME);
if (null != nameAtt)
{
elementType = nameAtt.getValue();
}
else
{
Attribute refAtt = current.getAttribute(XML_SCHEMA_ELEMREF);
if (null != refAtt)
{
elementType = refAtt.getValue();
}
else
{
throw new
SchemaException(ReqXML.getStringProperty(BAD_SCHEMA_ELEMENT_LACKS_NAME_REF))
;
}
}
logger.debug("Adding " + elementType + " to the list.");
allowedChildren.add(elementType);
}
} // while
// cache the result
_cacheOfValidChildTypes.put(parent.getName(), allowedChildren);
}
else
{
throw new
SchemaException(ReqXML.getStringProperty(INVALID_REQUIREMENT_TYPE));
}
}
// else
// {
// logger.debug("Found list in cache, returning that.");
// }
return allowedChildren; // note that if (null == parentInSchema) then
allowedChildren is empty
// }
// return null;
}
/*package*/ Collection getAttributes(String requirementType)
{
Vector attributes = new Vector();
Element reqTypeElement = findRequirementInSchema(requirementType);
if (reqTypeElement != null)
{
List children = reqTypeElement.getChildren();
Iterator iter = children.iterator();
while(iter.hasNext())
{
Element child = (Element)iter.next();
if (child.getName().equals(XML_SCHEMA_ATTRIB))
{
attributes.add(child);
}
}
return attributes;
}
return null;
}
private Element findRequirementInSchema(String reqName)
{
return findRequirementInSchema(_schema.getRootElement(), reqName);
}
/**
* Recursively performs a depth first search of the schema.
*/
private Element findRequirementInSchema(Element searchRoot, String
reqName)
{
List children = searchRoot.getChildren();
Iterator iter = children.iterator();
while (iter.hasNext())
{
Element current = (Element)iter.next();
Attribute nameAtt = current.getAttribute(XML_SCHEMA_ATT_NAME);
if (null != nameAtt)
{
String name = nameAtt.getValue();
if (name.equals(reqName))
{
return current; // found it
}
else
{
Element found = findRequirementInSchema(current, reqName);
if (null != found)
{
return found;
}
}
}
} // while
return null;
}
class SchemaException extends Exception
{
public SchemaException()
{
}
public SchemaException(String message)
{
super(message);
}
}
} // class schema
The other classes (subclassing JTree, add dnd and popups etc) are pretty
standard really.
-----Original Message-----
From: adam flinton [mailto:aflinton at armature.com]
Sent: 24 May 2001 14:59
To: 'Kenworthy, Edward'; 'Matthew MacKenzie'; Richard Cook; adam flinton
Cc: JDOM-Interest
Subject: RE: [jdom-interest] Has any one done a JDOM <> JTreeModel
adaptor
Could you possibly send me the code?
In essence we have 2 parts of which it appears that you have 1
1) Create a (J)DOM Tree from a DTD. We have this in XML-DBMS already as part
of the "GenerateMapFromDTD"
2) "GUI-ize" the tree & make it easy for Users (which is what you appear to
have).
I have a bunch of code for a generic "Container" which has a Jtree on the
left hand side & a JPanel on the right. You load up stuff like the button
bar etc via a properties file (which can be got via http). I am think about
building a generic JDOM based XML<>GUI which I can **then** extend to use
for Map building (in XML-DBMS). The reason is that @ the moment we have 2
DTD'es (V1 & V2) & I'd like to be able to suport both based upon the DTD.
Other people could then extend the generic XML<>GUI for their own uses.
TIA
Adam
> -----Original Message-----
> From: Kenworthy, Edward [mailto:edward.kenworthy at exchange.co.uk]
> Sent: 24 May 2001 07:29
> To: 'Matthew MacKenzie'; Richard Cook; adam flinton
> Cc: JDOM-Interest
> Subject: RE: [jdom-interest] Has any one done a JDOM <> JTreeModel
> adaptor
>
>
> I've actually done this already (except I do not display
> attributes in the
> tree as I didn't think that was appropriate). But I do have
> different icons
> for each element and I allow drag and drop (both move and copy).
>
> The way I did it was by writing my own XMLDocument class to wrap JDoms
> document. This XMLDocument class handles schemas and queries
> (eg can this
> element be added as a child of this element) and it also
> supports Document
> Listeners.
>
> I then have a second class, XMLDocTreeModel which implements
> TreeModel.
>
> A third class extends JTree to add in the custom renderers,
> drag and drop
> support and popup menu.
>
> -----Original Message-----
> From: Matthew MacKenzie [mailto:matt at xmlglobal.com]
> Sent: 23 May 2001 16:04
> To: Richard Cook; adam flinton
> Cc: JDOM-Interest
> Subject: RE: [jdom-interest] Has any one done a JDOM <> JTreeModel
> adaptor
>
>
> I would like a tree model that is capable of showing the
> whole XML document
> in its entirety, with configurables for assigning icons,
> etceteras. I think
> this would be useful because quite often in my job I have to
> show raw XML in
> a GUI to allow the user to either directly edit a piece of
> the XML, or use
> DnD to pull pieces of an XML tree into another tree or table.
> I think the
> M$ XML Notepad displays XML like this. Of course, the model
> should also be
> configurable enough that it can suppress comments, processing
> instructions,
> etceteras, that the programmer doesn't want displayed.
>
> I'll probably get around to this sometime, I'll probably
> start by toying
> around with the Sun examples of doing this with a DOM.
>
> -Matt
>
> -----Original Message-----
> From: Richard Cook [mailto:rpc at prismtechnologies.com]
> Sent: Wednesday, May 23, 2001 7:05 AM
> To: Matthew MacKenzie; adam flinton
> Cc: JDOM-Interest
> Subject: RE: [jdom-interest] Has any one done a JDOM <> JTreeModel
> adaptor
>
>
> I'm not sure why you want to display the whole document as a
> tree. Maybe a
> JtreeTable or similar would be better with attrs in the
> table. For mixed
> content, you could provide some view which would depend on
> the application
> (show the raw structure, simple concatenation, convert to html, ...)
>
> getChild() doesn't have to return a JDOMNode() to give a
> readable String,
> you might find you create new JDOMNodes each time you expand a node
> (consider expand/contract/expand same node), so you might provide some
> variation on singleton if this was the case, or you can just
> use a different
> cell renderer to display the correct text and return the Element from
> getChild
>
> class ElementRenderer extends javax.swing.JLabel implements
> javax.swing.tree.TreeCellRenderer {
> public java.awt.Component getTreeCellRendererComponent(
> javax.swing.JTree tree, Object value, boolean selected,
> boolean expanded, boolean leaf, int row, boolean hasFocus)
> {
> setText(((Element) value).getName());
> return this;
> }
> }
>
> then
> /* JTree tree */
> tree.setCellRenderer(new ElementRenderer());
>
> I'd consider using a real adapter to give a more ideal
> interface to the JDOM
> document to make the treemodel or whatever easier to write.
>
> If only someone was working on V2 of a Java XML book with a
> Jtree example
> running through it.
>
>
> -----Original Message-----
> From: jdom-interest-admin at jdom.org
> [mailto:jdom-interest-admin at jdom.org]On
> Behalf Of Matthew MacKenzie
> Sent: Tuesday, May 22, 2001 9:36 PM
> To: Richard Cook; adam flinton
> Cc: JDOM-Interest
> Subject: RE: [jdom-interest] Has any one done a JDOM <>
> JTreeModel adaptor
>
> Richard,
>
> How about something along these lines:
>
> public Object getChild(Object parent, int index) {
> int atts = ((Element)parent).getAttributes().size();
> int mcon = ((Element)parent).getMixedContent().size();
> if (index <= atts) {
> Attribute att =
> (Attribute)((Element)parent).getAttributes().get(index);
> return new JDOMNode(att);
> }
> else {
> return new
> JDOMNode(((Element)parent).getMixedContent().get(index-atts));
> }
> }
> public int getChildCount(Object parent) {
> int atts = ((Element)parent).getAttributes().size();
> int mcon = ((Element)parent).getMixedContent().size();
> return atts + mcon;
> }
>
>
> To properly display a whole XML tree, I need to tree the attributes as
> children, which means they have to be figured in to
> getChildCount. I also
> need to handle CDATA, Entity, Comment, etceteras..which necessitates
> creating a JDOMNode that can properly wrap all of the objects
> (Attribute,
> DocType, etc.) and return a human readable toString(). The
> other problem,
> which I am not sure exists in JDOM, is the occurence of
> multiple text nodes
> (String objects in JDOM). It looks stupid in a tree to have them all
> seperated:
>
> element: Root
> element: Kid
> #attr: id=1
> #text: fubar
> #cdata: <h1>hello</h1>
> #text: was his name.
>
> This is all do-able, and the JDOMNode idea is solid...I just
> read an example
> of doing this very thing using a DOM node, the only
> difference is that they
> didn't support attributes.
>
> Is it against the XML spec to include attribute objects in
> getMixedContent()? I think that would be very useful.
>
>
> -Matt
>
> -----Original Message-----
> From: Richard Cook [mailto:rpc at prismtechnologies.com]
> Sent: Tuesday, May 22, 2001 11:25 AM
> To: Matthew MacKenzie; adam flinton
> Subject: RE: [jdom-interest] Has any one done a JDOM <> JTreeModel
> adaptor
>
>
> Expect someone has done it properly. I posted this a few
> weeks back. The
> tree model asks for children one by one so you can return
> what you want in
> getChild(). I suspect there is lots of inefficiency because
> of the partial
> lists built in getChildren().
>
>
> Depends on what you need, but say you want to display the
> elements could you
> not just implement TreeModel:
>
> class JDOMTreeModel implements TreeModel {
> private Element root;
> public JDOMTreeModel(Element root) {
> this.root = root;
> }
> public Object getChild(Object parent, int index) {
> return ((Element) parent).getChildren().get(index);
> }
> public int getChildCount(Object parent) {
> return ((Element) parent).getChildren().size();
> }
> public int getIndexOfChild(Object parent, Object child) {
> return ((Element) parent).getChildren().indexOf(child);
> }
> public Object getRoot() {
> return root;
> }
> public boolean isLeaf(Object node) {
> return false; // whatever
> }
> // etc
> }
>
> then TreeModel mdl = new JDOMTreeModel(doc.getRootElement());
> JTree tree = new JTree(mdl)
>
>
> saves serialising the JDOM tree or having to walk it. You can
> change the
> cell renderer to display the element name rather than the
> toString(), or
> wrap the "Element" in getChild() if necessary [ return new
> MyNode(((Element)
> parent).getChildren().get(index)) ]. If you need attrs
> there'd be a bit more
> work.
>
> -----Original Message-----
> From: jdom-interest-admin at jdom.org
> [mailto:jdom-interest-admin at jdom.org]On
> Behalf Of Kiss Gábor
> Sent: Thursday, May 03, 2001 8:11 AM
> To: jdom-interest at jdom.org
> Subject: [jdom-interest] jdom tree conversion to a JTree
>
>
> How can I convert a jdom tree to a JTree?
>
> Thanks
>
> Kiss Gábor
>
> _______________________________________________
> To control your jdom-interest membership:
> http://lists.denveronline.net/mailman/options/jdom-interest/yo
> uraddr at yourhos
> t.com
>
>
>
>
> -----Original Message-----
> From: jdom-interest-admin at jdom.org
[mailto:jdom-interest-admin at jdom.org]On
Behalf Of Matthew MacKenzie
Sent: Tuesday, May 22, 2001 6:48 PM
To: adam flinton; JDOM Mailing List (E-mail)
Subject: RE: [jdom-interest] Has any one done a JDOM <> JTreeModel adaptor
I wrote the JTreeOutputter for simple display purposes, but lately I have
found the need to keep a JDOM in sync with the tree contents. I have never
wrote a JTreeModel, is it hard? I might be able to whip one together fast
depending on the API.
-Matt
-----Original Message-----
From: jdom-interest-admin at jdom.org
[mailto:jdom-interest-admin at jdom.org]On Behalf Of adam flinton
Sent: Tuesday, May 22, 2001 10:07 AM
To: JDOM Mailing List (E-mail)
Subject: [jdom-interest] Has any one done a JDOM <> JTreeModel adaptor
Dear All,
I was just wondering if anyone has done a JDOM tree <> TreeModel adaptor
such that I can use a JDOM Model to directly interface with a JTree.
i.e. I am looking @ doing a designer tool for XML-DBMS & was very impressed
by the **lack of code** within the JTreeOutputter however it strikes me as
silly to store in effect 2 trees which I then have to keep in sync.
Any thoughts?
TIA
Adam Flinton
_______________________________________________
To control your jdom-interest membership:
http://lists.denveronline.net/mailman/options/jdom-interest/youraddr@yourhos
t.com
_______________________________________________
To control your jdom-interest membership:
http://lists.denveronline.net/mailman/options/jdom-interest/youraddr@yourhos
t.com
_______________________________________________
To control your jdom-interest membership:
http://lists.denveronline.net/mailman/options/jdom-interest/youraddr@yourhos
t.com
_______________________________________________
To control your jdom-interest membership:
http://lists.denveronline.net/mailman/options/jdom-interest/youraddr@yourhos
t.com
_______________________________________________
To control your jdom-interest membership:
http://lists.denveronline.net/mailman/options/jdom-interest/youraddr@yourhos
t.com
More information about the jdom-interest
mailing list