[jdom-interest] Has any one done a JDOM <> JTreeModel adaptor

Kenworthy, Edward edward.kenworthy at exchange.co.uk
Thu May 24 07:12:25 PDT 2001


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



More information about the jdom-interest mailing list