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.mdr.test;

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

import org.xml.sax.*;
import org.w3c.dom.*;

import javax.xml.parsers.*;

import org.netbeans.lib.jmi.util.*;

import javax.jmi.reflect.*;
import javax.jmi.model.*;

public class XMIComparator extends Object {
     
    // a character used to mark start of an element name
    private static final char ELEMENT_START = '[';
    // a character used to mark end of an element name
    private static final char ELEMENT_END = ']';
    // an auxiliar separator used to create code of a tree
    private static final char LEFT_SEPARATOR = '(';
    // an auxiliar separator used to create code of a tree
    private static final char RIGHT_SEPARATOR = ')';
    // an auxiliar separator used to enclose a comment
    private static final char COMMENT_MARKER = '#';

    private CodeComparator codeComparator = new CodeComparator ();
    
    // init .....................................................................
    
    public XMIComparator() {
    }
    
    // methods ..................................................................
            
    /**
     * Compares content of two XMI files. Both files are parsed by DOM parser. 
     * Nodes representing content sections are found in created structures and
     * their content is coded into a String value. The codes equals iff both trees 
     * are isomorphous. Note that (for now) attribute values are not included in tree
     * codes and that whitespace characters are excluded when content of TEXT_NODE
     * is added as a part of a resulting code.
     *
     * @param streamOne InputSource providing the first XMI documnet
     * @param streamTwo InputSource providing the second XMI documnet
     *
     * @return null if both documents are logically identical, otherwise an error message
     */
    public String compareDocuments (InputSource streamOne, InputSource streamTwo) {
        try {
            Document docOne = parse (streamOne, null);
            Document docTwo = parse (streamTwo, null);
            Node xmiNode = findSubnodeByName (docOne, "XMI");
            Node contentNode = findSubnodeByName (xmiNode, "XMI.content");            
            String codeOne = treeCode (contentNode);
            
            xmiNode = findSubnodeByName (docTwo, "XMI");
            contentNode = findSubnodeByName (xmiNode, "XMI.content");            
            String codeTwo = treeCode (contentNode);
            
            if (removeComments (codeOne).equals (removeComments (codeTwo)))
                return null;
            String el = findDifference (codeOne, codeTwo);
            return "Trees are not identical, element: " + el;
        } catch (Exception e) {
            e.printStackTrace ();
            return "Cannot parse documnet";
        }
    }
    
    /**
     * Finds the first difference in two codes and detects the name of the nearest
     * element that encloses the detected position.
     */
    private String findDifference (String c1, String c2) {
        int x = 0;
        int length = Math.min (c1.length (), c2.length ());
        for (x = 0; x < c1.length (); x++)
            if (c1.charAt (x) != c2.charAt (x))
                break;
        while (c1.charAt (x) != ELEMENT_START)
            x--;
        String name = "";
        x++;
        while (c1.charAt (x) != ELEMENT_END) {
            name = name + c1.charAt (x);
            x++;
        }
        return name;
    }
    
    /**
     * Returns direct child node given by its name.
     */
    private Node findSubnodeByName (Node node, String name) {
        NodeList subNodes = node.getChildNodes ();
        for (int x = 0; x < subNodes.getLength (); x++) {
            Node temp = subNodes.item (x);
            if (temp.getNodeName ().equals (name))
                return temp;
        } // for
        return null;
    }    
        
    /**
     * Returns a code for a tree given by its root node.
     */
    private String treeCode (Node node) {
        if (node.getNodeType () == Node.TEXT_NODE) {
            String text = node.getNodeValue ();
            String res = "";
            for (int x = 0; x < text.length (); x++)
                if (!Character.isWhitespace (text.charAt (x)))
                    res = res + text.charAt (x);
            return res;
        }
        if (node.getNodeType () != Node.ELEMENT_NODE)
            return ""; // ignore non-text and non-element nodes
        List list = new LinkedList ();
        String nodeName = node.getNodeName ();        
        NamedNodeMap attrs = node.getAttributes ();        
        String xmiId = null;
        for (int x = 0; x < attrs.getLength (); x++) {
            Node attr = attrs.item (x);
            String name = attr.getNodeName ();
            if (name.equals ("xmi.id"))
                xmiId = attr.getNodeValue ();
            String value = attr.getNodeValue ();            
            if (value.startsWith ("xmi.")) {
                value = "xmi.";
            } // if
            list.add (name); // [PENDING] value not added yet !!!
        } // for
        String attributesCode = listToString (list);
        list = new LinkedList ();
        NodeList subNodes = node.getChildNodes ();
        for (int x = 0; x < subNodes.getLength (); x++) {
            String s = treeCode (subNodes.item (x));
            if (s.length () > 0)
                list.add (s);
        } // for
        String subNodesCode = listToString (list);
        String comment = "";
        if (xmiId != null)
            comment = " " + COMMENT_MARKER + xmiId + COMMENT_MARKER;
        return LEFT_SEPARATOR + " " + ELEMENT_START + nodeName + comment + ELEMENT_END + attributesCode + 
            " " + subNodesCode + RIGHT_SEPARATOR;
    }
    
    /**
     * Sorts all Strings in a List and returns their contatenation.
     */
    private String listToString (List list) {
        Collections.sort (list, codeComparator);
        String res = "";
        Iterator iter = list.iterator ();
        while (iter.hasNext ()) {
            res = res + " " + (String) iter.next ();
        }
        return res;
    }
    
    /**
     * Parses XMI document into DOM tree structure.
     */
    private Document parse (InputSource is, String encoding) throws IOException, SAXException, ParserConfigurationException {
        Document result = null;
        DocumentBuilderFactory bfact = DocumentBuilderFactory.newInstance();
        bfact.setValidating(false);
        DocumentBuilder docBuilder = bfact.newDocumentBuilder();
	docBuilder.setEntityResolver(DummyER.getInstance());        
        if (encoding != null) is.setEncoding(encoding);
        result = docBuilder.parse(is);
        return result;
    }
    
    private static String removeComments (String code) {
        StringBuffer res = new StringBuffer (code.length ());
        boolean inComment = false;
        for (int x = 0; x < code.length (); x++) {
            char c = code.charAt (x);
            if (c == COMMENT_MARKER)
                inComment = !inComment;
            else if (!inComment)
                res.append(c);
        } // for
        return res.toString ();
    }
    
    // (Dummy) entity resolver class
    private static class DummyER implements EntityResolver {
        private static EntityResolver instance = new DummyER();

        public static EntityResolver getInstance() {
            return instance;
        }

        public InputSource resolveEntity(String publicID, String systemID) {
            return new InputSource(new StringReader(""));
        }
    }
        
    private static class CodeComparator implements Comparator {    
        
        public int compare(Object obj1, Object obj2) {
            String s1 = removeComments ((String) obj1);
            String s2 = removeComments ((String) obj2);
            return s1.compareTo(s2);
        }        
                
    }
    
}
... 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.