alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.lib.jmi.xmi;

import java.io.*;
import java.util.*;
import java.net.*;

import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

import org.netbeans.api.xmi.*;

import javax.jmi.reflect.*;
import javax.jmi.model.*;
import org.netbeans.lib.jmi.util.Logger;
import org.netbeans.lib.jmi.util.DebugException;

public class XmiSAXReader extends DefaultHandler {

    private URL docURL = null;
    
    // currently processed element
    private XmiElement currentElement;
    // context
    private XmiContext context;
    
    // differences that have to be applayed on the content of read XMI
    private HashMap diffs = null;
    // indicates if elements of Content section are being currently read
    private boolean contentIsRead = false;
    // indicates if a predecessor of a read sub-element has been deleted by a diference
    private boolean elementIsDeleted = false;
    // timestamp of a difference that has been or is being applyed
    private int lastTimeStamp = 0;
    // stack used to to track information needed to applay differences
    private Stack stack = new Stack ();
    // document locator
    private Locator locator = null;
    // root element name ("XMI" in case of XMI 1.1, "ns:XMI" or "ns:ClassName" in case of XMI 2.0)
    private String rootElementName = null;

    // possible elemnet of the stack, represents a deleted XMI element
    private final Object DELETED = new Integer (0);
    // an empty element, pushed to the stack if no information is needed to store in a given step
    private final Object EMPTY = new Integer (1);
    
    private XMIInputConfig config;
    
    // init .....................................................................

    public XmiSAXReader () {
        this (null, null);
    }
    
    public XmiSAXReader (XMIInputConfig config) {
        this (null, config);
    }
    
    public XmiSAXReader (XmiContext context, XMIInputConfig config) {
        this.context = context;
        if (config == null)
            this.config = new InputConfig ();
        else
            this.config = config;
    }
    
    // methods ..................................................................    
    
    public Collection read (InputStream input, String uri, RefPackage[] extents, String encoding)
        throws IOException, SAXException, ParserConfigurationException {            
        InputSource is = new InputSource (input);
        if (encoding != null) {
            is.setEncoding(encoding);
        }
        if (uri != null) {
            is.setSystemId(uri);
            try {
                docURL = new URI (uri).toURL ();
            } catch (URISyntaxException e) {
            } catch (MalformedURLException e) {
            } catch (IllegalArgumentException e) {
            }
        }
        return read (is, extents);
    }
    
    public Collection read (URL url, RefPackage[] extents, String encoding)
        throws IOException, SAXException, ParserConfigurationException {
        docURL = url;    
        InputSource is = new InputSource (url.toString ());                        
        if (encoding != null) 
            is.setEncoding(encoding);
        return read (is, extents);
    }
     
    public Collection read (URL url, RefPackage[] extents, String encoding, HashMap diffs)
        throws IOException, SAXException, ParserConfigurationException {
        this.diffs = diffs;
        return read (url, extents, encoding);
    }
    
    public Collection read (InputSource input, RefPackage[] extents) 
        throws IOException, SAXException, ParserConfigurationException {
        if (diffs == null)
            Logger.getDefault().log ("XMI reader started");
        long time = System.currentTimeMillis ();
        if (docURL == null) {
            String systemId = input.getSystemId();
            if (systemId != null) {
                try {
                    docURL = new URL (systemId);
                } catch (MalformedURLException e) {
                } catch (IllegalArgumentException e) {
                }
            }
        }
        if (context == null)
            context = new XmiContext (extents, docURL, config);
        SAXParserFactory factory = SAXParserFactory.newInstance ();
        factory.setValidating (false);
        SAXParser saxParser = factory.newSAXParser ();
        saxParser.parse (input, this);                
        time = System.currentTimeMillis () - time;
        if (diffs == null)
            Logger.getDefault().log ("finished, TIME: " + time/1000.0 + "[s]");
        return context.getOutermostObjects ();        
    }
    
    void initConsumer (RefPackage pkg) throws SAXException {
        context = new XmiContext (new RefPackage [] {pkg}, null, config);
    }
    
    
    public void startDocument () throws SAXException {
        rootElementName = null;
    }

    /*
    public void endDocument () throws SAXException {       
    }
     */

    public void startElement (String namespaceURI, String sName, String qName, Attributes attrs) 
    throws SAXException {
        
        try {
        
        // Logger.getDefault().log ("s: " + qName);

        if (elementIsDeleted) {
            stack.push (EMPTY);
            return;
        } else if (rootElementName == null) {
            rootElementName = qName;
            currentElement = new XmiElement.Document (null, context, qName, attrs);
            return;
        } else if (currentElement == null) {
            throw new DebugException("Not an XMI document. Root element must be <"+XmiConstants.XMI_ROOT+">");
        } else if (qName.equals (XmiConstants.XMI_CONTENT)) {
            stack.push (EMPTY);
            contentIsRead = true;
        } else if ((contentIsRead && qName.equals(XmiConstants.XMI_EXTENSION)) ||
                    (!contentIsRead && qName.equals(XmiConstants.XMI_EXTENSIONS))) {
            stack.push (DELETED);
            elementIsDeleted = true;
            return;
        } else if ((contentIsRead) && (diffs != null)) {            
            // check if there are some differences to apply
            String xmiId = attrs.getValue (XmiConstants.XMI_ID);
            List list = null;
            if (xmiId != null)
                list = (List) diffs.get (xmiId);
            if (list != null) {
                String elementName = null;
                Iterator iter = list.iterator ();
                List modifs = new LinkedList ();
                while (iter.hasNext ()) {                    
                    XmiElement.Difference.Diff diff = (XmiElement.Difference.Diff) iter.next ();
                    if (diff.timeStamp <= lastTimeStamp)
                        continue;
                    switch (diff.kind) {
                        case XmiElement.Difference.Diff.DELETE:
                            stack.push (DELETED);
                            elementIsDeleted = true;
                            return;
                        case XmiElement.Difference.Diff.ADD:
                            modifs.add (diff);
                        break;
                        case XmiElement.Difference.Diff.REPLACE:
                            XmiElement.Difference.Item item = (XmiElement.Difference.Item) diff.items.removeFirst ();
                            qName = item.qName;
                            attrs = item.attrs;
                            elementName = qName;
                            diff.items.removeLast ();
                            modifs.add (diff);
                        break;
                    } // switch
                } // while
                stack.push (new StackElement (modifs, elementName));
            } else {
                stack.push (EMPTY);
            } // if
        } // if
        
        currentElement = currentElement.startSubElement (qName, attrs);
        if (currentElement == null)
            // should not occur
            throw new DebugException ("XmiElement.startSubElement("+qName+", ..) returned null");
        
        if (contentIsRead && (diffs != null))
            applayDiffs ();
        
        } catch (Exception e) {
            
            e.printStackTrace ();
            
            Logger.getDefault().annotate(e, "Error parsing XMI" + getLineNumberErrorString());
            throw (SAXException) Logger.getDefault().annotate(new SAXException("XMI parsing error" + getLineNumberErrorString() + ": " + (e.getMessage() != null ? e.getMessage() : e.toString()), e), e);
        }
        
    }

    public void endElement (String namespaceURI, String sName, String qName) throws SAXException {
        // Logger.getDefault().log ("e: " + qName);
        
        try {
        
        if (elementIsDeleted) {
            if (stack.pop () == DELETED)
                elementIsDeleted = false;
            return;
        }

        if (qName.equals (XmiConstants.XMI_CONTENT))
            contentIsRead = false;
        else {
            if (contentIsRead && (diffs != null)) {
                Object obj = stack.pop ();
                if ((obj instanceof StackElement) && ((StackElement) obj).replacedName != null)
                    qName = ((StackElement) obj).replacedName;
            }
        } // if
        
        currentElement = currentElement.endElement (qName);
        if ((currentElement == null) && !qName.equals (rootElementName))
            // should not occur
            throw new DebugException ("XmiElement.endElement("+qName+") returned null");
        
        if (contentIsRead && (diffs != null))
            applayDiffs ();
        
        } catch (Exception e) {
            
            e.printStackTrace ();
            String message = "XMI parsing error" + getLineNumberErrorString() + ": " + (e.getMessage() != null ? e.getMessage() : e.toString());
            Logger.getDefault().log(Logger.WARNING, message);
            SAXException ne = new SAXException(message, e);
            throw (SAXException) Logger.getDefault().annotate(ne, e);
        }
        
    }

    public void characters (char buf[], int offset, int len) throws SAXException {
        
        String s = new String (buf, offset, len);        
        
        try {
        
        if (!elementIsDeleted) {
            if (currentElement != null)
                currentElement.characters (buf, offset, len);
        }
        
        } catch (Exception e) {
            
            e.printStackTrace ();
            
            SAXException ne = new SAXException("XMI parsing error" + getLineNumberErrorString() + ": " + (e.getMessage() != null ? e.getMessage() : e.toString()), e);
            Logger.getDefault().annotate(ne, e);
            throw (SAXException) Logger.getDefault().annotate(ne, "XMI parsing error" + getLineNumberErrorString());
        }
    
    }

    public void setDocumentLocator (Locator locator) {
        this.locator = locator;
    }
    
    /**
     * Produces text like "at line: #" useful for error messages. Produces
     * the empty string, if no locator was set.
     */
    private String getLineNumberErrorString() {
      if ( locator != null ) {
        return " at line: " + locator.getLineNumber();
      } else {
        return "";
      }
    }
    
    public InputSource resolveEntity (String publicID, String systemID) {
        Logger.getDefault().log ("resolving reference: " + publicID + ", " + systemID);
        return new InputSource (new StringReader (""));
    }

    private void applayDiffs () throws SAXException {
        Object elem = stack.peek ();
        if (elem != EMPTY) {
            ((StackElement) elem).position++;
            Iterator iter = ((StackElement) elem).diffs.iterator ();
            while (iter.hasNext ()) {
                XmiElement.Difference.Diff diff = (XmiElement.Difference.Diff) iter.next ();
                if ((diff.position == ((StackElement) elem).position) && (diff.timeStamp > lastTimeStamp)) {
                    int temp = lastTimeStamp;
                    lastTimeStamp = diff.timeStamp;
                    addContent (diff);
                    lastTimeStamp = temp;
                }
            } // while
        } // if (item != null)
    }
    
    private void addContent (XmiElement.Difference.Diff diff) throws SAXException {
        Iterator iter = diff.items.iterator ();
        while (iter.hasNext ()) {
            Object obj = iter.next ();
            if (obj instanceof XmiElement.Difference.Item) {
                XmiElement.Difference.Item item = (XmiElement.Difference.Item) obj;
                if (item.isStart) {
                    startElement (null, "", item.qName, item.attrs); 
                } else {
                    endElement (null, "", item.qName);
                }
            } else
                characters (((String) obj).toCharArray (), 0, ((String) obj).length ());
        } // while
    }
    
    private static class StackElement {
        public int position = 0;
        public List diffs;
        public String replacedName;
        
        public StackElement (List diffs, String replacedName) {
            this.diffs = diffs;
            this.replacedName = replacedName;
        }        
    } // StackElement
    
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.