 /** javadox2umldiagrams

 * this class converts xml-files created by the javadoclet javadox to the xml-format which 

 * is read by anulcad.

**/

import javax.xml.parsers.DocumentBuilderFactory;  

import javax.xml.parsers.FactoryConfigurationError;  

import javax.xml.parsers.ParserConfigurationException;

 

import javax.xml.parsers.DocumentBuilder;  

import org.xml.sax.SAXException;  

import org.xml.sax.SAXParseException;  

import com.sun.xml.tree.*;



import org.w3c.dom.*;



import java.io.*;

import java.io.IOException;

import java.util.StringTokenizer;



class Javadox2Umldiagrams

{

	static XmlDocument doc;

	

	static void go(Document indoc, String fname) throws ParserConfigurationException

	{

		// create DOM for output

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance ();

		DocumentBuilder        db  = dbf.newDocumentBuilder ();

		                       doc = (XmlDocument)db.newDocument ();

		Element                ud  = doc.createElement ("umldiagrams");

		Element                cd  = doc.createElement ("classdiagram");

		doc.appendChild(ud);

		ud.appendChild(cd);

		Attr tmp;



		// parse all packages creating a flat one-level package strucuture

		NodeList nl = indoc.getElementsByTagName("package");

    for (int i=0; i<nl.getLength(); i++)

		{

			Element pn = (Element)nl.item(i);

			// add package

			NodeList nltt = getNodeListWithTag(pn,"packageName");

			String packageName = getTextOfNodeI(nltt,0);

			Element p = createElWithID("package",packageName, packageName, pn);

			Element classes = doc.createElement("classes");

			p.appendChild(classes);

			cd.appendChild(p);

			

			// parse all class-elements in that package

			NodeList cnl = pn.getElementsByTagName("class");

	    for (int j=0; j<cnl.getLength(); j++)

			{

				Element cl = (Element)cnl.item(j);

				String classname = getTextOfFirstTag(cl,"className");

				String id        = getTextOfFirstTag(cl,"classQualifiedName");

				String type      = getTextOfFirstTag(cl,"classType");

				if (type==null || type.equals("")) type="class";

				Element c = createElWithID(type, classname,id,cl);

				

		    // append superclass-element

				String sclass = getTextOfFirstTag(cl,"superClass");

				if (sclass!=null && !sclass.equals("") && !sclass.equals("java.lang.Object"))

					c.appendChild(createRel("generalization",id,sclass));

				

				// append interface-links (implements ...)

				NodeList inl = cl.getElementsByTagName("interface");

				for (int k=0; k<inl.getLength(); k++) c.appendChild(createRel("generalization",id,getText(inl.item(k))));

				

				// search for special javadoc-comments describing geometry

				String fullDescription = getTextOfFirstTag(cl,"fullDescription");

				if (fullDescription!=null && !fullDescription.equals(""))

				{

					StringTokenizer fullDescriptionLines = new StringTokenizer(fullDescription,"\n");

					while(fullDescriptionLines.hasMoreTokens())

					{

						String fdLine = fullDescriptionLines.nextToken();

						if (fdLine.trim().startsWith("geometry("))

						{

							String gParams = fdLine.substring(10,fdLine.length()-1);

							System.out.println("geometry found, extracing params: " + gParams);

							StringTokenizer gParamList = new StringTokenizer(gParams,",=");

							while(gParamList.hasMoreTokens())

							{

								String pName  = gParamList.nextToken().trim();

								String pValue = gParamList.nextToken().trim();

								System.out.println("Trying to set " + pName + " to " + pValue);

								c.setAttribute(pName,pValue);

							}

						}

					}

				}

				

				// add content

				Element operations = doc.createElement("operations");

				

				// add constructors

				NodeList rnl = cl.getElementsByTagName("constructor");

				for (int l=0; l<rnl.getLength(); l++)

				{

					Element op = (Element)rnl.item(l);

					String opname = getTextOfFirstTag(op,"constructorName");

					String sig    = getTextOfFirstTag(op,"signature");

					Element o = createElWithID("operation",opname, opname + sig, op);

					operations.appendChild(o);

				}

				

				// add methods/operations

				NodeList onl = cl.getElementsByTagName("method");

				for (int l=0; l<onl.getLength(); l++)

				{

					Element op = (Element)onl.item(l);

					String methodname = getTextOfFirstTag(op,"methodName");

					String sig        = getTextOfFirstTag(op,"signature");

					String returntype = getTextOfFirstTag(op,"returnType");

					Element o = createElWithID("operation",methodname, methodname + sig, op);

					o.setAttribute("signature",sig);

					o.setAttribute("returntype",returntype);

					// parameters are ignored, may be added later

					operations.appendChild(o);

				}

				if (onl.getLength()>0 || rnl.getLength()>0) c.appendChild(operations);



				// add properties/attributes

				Element attributes = doc.createElement("attributes");

				NodeList anl = cl.getElementsByTagName("property");

				for (int l=0; l<anl.getLength(); l++)

				{

					Element attr = (Element)anl.item(l);

					String propname = getTextOfFirstTag(attr,"propertyName");

					String proptype = getTextOfFirstTag(attr,"propertyType");

					Element a = createElWithID("attribute",propname, id + "." + propname, attr);

					a.setAttribute("type",proptype);

					attributes.appendChild(a);

				}

				if (anl.getLength()>0) c.appendChild(attributes);

				

				// finally add the class

				classes.appendChild(c);

			} // foreach class



			// implements -> association

			// extends -> generalization

			// method -> operations.operation

    } // foreach package

    try

    {

    	FileWriter fw = new FileWriter(fname);

    	doc.write(fw, "UTF-8");

	    fw.flush();

    }

    catch (IOException e)

    {

    	System.out.println("IOException: " + e.getMessage());

    }

	}

	

	// return the text of the node

	public static String getText(Node n)

	{

		// System.out.println("getText(" + n.toString() + ")");

		if (n==null) return new String();

		else         return n.getFirstChild().getNodeValue();

	}

	

	// returns the first child named tag

	public static Element getElementI(NodeList nl, int i)

	{

		return (Element)nl.item(i);

	}

	

	public static String getTextOfElementI(NodeList nl, int i)

	{

		return getText(nl.item(i));

	}

	

	public static NodeList getNodeListWithTag(Node n, String tag)

	{

		if (n.getNodeType()==Node.ELEMENT_NODE) return ((Element)n).getElementsByTagName(tag);

		else                                    return null;

	}



	// returns the text contained in the first node of the list named tag

	public static String getTextOfNodeI(NodeList nl, int i)

	{

		return getText(nl.item(i));

	}

	

	public static String getTextOfFirstTag(Node n, String tag)

	{

		return getTextOfNodeI(getNodeListWithTag(n,tag),0);

	}

	

	// creates an annotation-textnode from the subnode briefDescription of a node named description

	public static Element createAnn(Element e)

	{

		NodeList nl = e.getChildNodes();

		String description = null;

		for (int a=0; a<nl.getLength(); a++) 

		{

			if (nl.item(a).getNodeName().equals("description"))

			{

				description = getTextOfFirstTag(nl.item(a),"briefDescription");

				break;

			}

		}

		if (description==null) return null;

		Element annotation = doc.createElement("annotation");

		annotation.appendChild(doc.createCDATASection(description));

		return annotation;

	}

	

	// creates a relation-construct

	public static Element createRel(String relName, String from, String to)

	{

		Element r = doc.createElement(relName);

		String id = relName + "$" + from + "$" + to;

		r.setAttribute("name", id);

		r.setAttribute("id"   ,convert2id(id));

		r.setAttribute("to"   ,to);

		// System.out.println("RelCreated: " + r.toString());

		return r;

	}

	

	// creates an element with tag ename and name/id adding the annotation found at ann

	public static Element createElWithID(String ename, String name, String id, Element src)

	{

			Element e = doc.createElement(ename);

			e.setAttribute("name",name);

			e.setAttribute("id",convert2id(id));

			String vis = src.getAttribute("accessSpecifier");

			if (vis!=null && !vis.equals("")) e.setAttribute("visibility", vis);

			String stat = src.getAttribute("static");

			if (stat!=null && !stat.equals("")) e.setAttribute("static",stat);

			Element ann = createAnn(src);

			if (ann!=null) e.appendChild(ann);

			return e;

	}

	

	public static String convert2id(String str)

	{

		char[] chars = str.toCharArray();

		for (int i=0;i<chars.length;i++)

		{

			if (!Character.isLetterOrDigit(chars[i]) && chars[i]!='-' && chars[i]!='_' && chars[i]!='.')

			{

				chars[i]='_';

			}

		}

		return new String(chars);

	}

	

	// MAIN

	public static void main (String argv []) throws IOException

	{

		if (argv.length != 1) System.err.println ("Usage: cmd filename");

    try

		{

 		 	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    	DocumentBuilder builder        = factory.newDocumentBuilder();

			Document indoc                 = builder.parse( new File(argv[0]));

			go(indoc, argv[0]+".aud");

    }

		catch (SAXParseException spe)

		{

			// Error generated by the parser

			System.out.println ("\n** Parsing error" + ", line " + spe.getLineNumber () + ", uri " + spe.getSystemId ());

			System.out.println("   " + spe.getMessage() );

			// Use the contained exception, if any

			Exception  x = spe;

			if (spe.getException() != null) x = spe.getException();

			x.printStackTrace();

    }

    catch (SAXException sxe) // Error generated by this application

    {

       Exception  x = sxe;

       if (sxe.getException() != null) x = sxe.getException();

       x.printStackTrace();

    }

    catch (ParserConfigurationException pce) { pce.printStackTrace(); }

		catch (IOException ioe)                  { ioe.printStackTrace(); }



		// File      outputFile = new File(argv[0]+".out");

		// FileOutputStream fos = new FileOutputStream(outputFile);

		// out = new PrintStream(fos);

		// fos.close();

	}



}
