diff options
author | janeklb | 2006-01-30 19:23:57 +0000 |
---|---|---|
committer | janeklb | 2006-01-30 19:23:57 +0000 |
commit | 97db99f0c3a535f1387dca40e0ad6d61b1ff8db2 (patch) | |
tree | f7c90383a2b2dfce0c0cb65896b795eb3bf7bc87 | |
parent | cf0e31f70a90dc260d18d315d15e17daae71dc60 (diff) | |
download | eclipse.pde.ui-xml_compare.tar.gz eclipse.pde.ui-xml_compare.tar.xz eclipse.pde.ui-xml_compare.zip |
xml comparexml_compare
14 files changed, 2077 insertions, 0 deletions
diff --git a/ui/org.eclipse.pde.ui/icons/obj16/xml_text_node.gif b/ui/org.eclipse.pde.ui/icons/obj16/xml_text_node.gif Binary files differnew file mode 100644 index 0000000000..2a2b4b6f40 --- /dev/null +++ b/ui/org.eclipse.pde.ui/icons/obj16/xml_text_node.gif diff --git a/ui/org.eclipse.pde.ui/plugin.xml b/ui/org.eclipse.pde.ui/plugin.xml index b02b282aa2..c779b3a911 100644 --- a/ui/org.eclipse.pde.ui/plugin.xml +++ b/ui/org.eclipse.pde.ui/plugin.xml @@ -1759,6 +1759,13 @@ <contentTypeBinding
contentTypeId="org.eclipse.pde.bundleManifest"
structureMergeViewerId="org.eclipse.pde.internal.ui.compare.ManifestStructureMergeViewerCreator"/>
+ <viewer
+ extensions="xml,exsd"
+ class="org.eclipse.pde.internal.ui.compare.XMLStructureViewerCreator"
+ id="org.eclipse.pde.internal.ui.compare.XMLStructureViewerCreator" />
+ <contentTypeBinding
+ structureMergeViewerId="org.eclipse.pde.internal.ui.compare.XMLStructureViewerCreator"
+ contentTypeId="org.eclipse.pde.pluginManifest" />
</extension>
<extension
point="org.eclipse.compare.contentMergeViewers">
@@ -1769,6 +1776,13 @@ <contentTypeBinding
contentMergeViewerId="org.eclipse.pde.internal.ui.compare.ManifestContentMergeViewerCreator"
contentTypeId="org.eclipse.pde.bundleManifest"/>
+ <viewer
+ class="org.eclipse.pde.internal.ui.compare.XMLContentMergeViewerCreator"
+ extensions="xml,exsd"
+ id="org.eclipse.pde.internal.ui.compare.XMLContentMergeViewerCreator"/>
+ <contentTypeBinding
+ contentMergeViewerId="org.eclipse.pde.internal.ui.compare.XMLContentMergeViewerCreator"
+ contentTypeId="org.eclipse.pde.pluginManifest" />
</extension>
<extension
point="org.eclipse.ui.themes">
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java index 661a7c4902..58231f1b9d 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java @@ -122,6 +122,7 @@ public class PDEPluginImages { public static final ImageDescriptor DESC_OK_TRANSLATE_OBJ = create(PATH_OBJ, "ok_st_obj.gif"); //$NON-NLS-1$ public static final ImageDescriptor DESC_NO_TRANSLATE_OBJ = create(PATH_OBJ, "incomplete_tsk.gif"); //$NON-NLS-1$ public static final ImageDescriptor DESC_DISCOVERY = create(PATH_OBJ, "discovery.gif"); //$NON-NLS-1$ + public static final ImageDescriptor DESC_XML_TEXT_NODE = create(PATH_OBJ, "xml_text_node.gif"); //$NON-NLS-1$ /** * OVR16 */ diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/AbstractMatching.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/AbstractMatching.java new file mode 100644 index 0000000000..5ecbd85b30 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/AbstractMatching.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import java.util.ArrayList; +import java.util.Vector; + +import org.eclipse.compare.rangedifferencer.IRangeComparator; +import org.eclipse.compare.rangedifferencer.RangeDifference; +import org.eclipse.compare.rangedifferencer.RangeDifferencer; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * @version 1.0 + * @author + */ +public abstract class AbstractMatching { + + protected static final int NO_ENTRY = -1;//value with which fDT elements are initialized + protected static final String SIGN_ELEMENT= XMLStructureCreator.SIGN_ELEMENT; + int[][] fDT;//Distance Table; 1st index from fNLeft, 2nd index from fNRight + ArrayList[][] fDT_Matchings;//Mathing entries of children for a match. 1st index from fNLeft, 2nd index from fNRight + Vector fNLeft; + Vector fNRight; + Vector fMatches; + + /* methods used for match */ + + /* finds all the leaves of a tree and puts them in a vector */ + protected void findLeaves(XMLNode root, ArrayList leaves) { + if (isLeaf(root)) { + leaves.add(root); + } else { + Object[] children = root.getChildren(); + for (int i=0; i<children.length; i++) + findLeaves((XMLNode) children[i], leaves); + } + } + + /* true if x is a leaf */ + protected boolean isLeaf(XMLNode x) { + if (x == null) return true; + return x.getChildren() == null || x.getChildren().length <= 0; + } + + /* Numbers all nodes of tree. The number of x is its index in the vector numbering */ + protected void numberNodes(XMLNode root, Vector numbering) { + if (root != null) { + numbering.add(root); + Object[] children = root.getChildren(); + if (children != null) { + for (int i=0; i<children.length; i++) + numberNodes((XMLNode) children[i], numbering); + } + } + } + + /* counts # of nodes in tree including root */ + protected int countNodes(XMLNode root) { + if (root == null) return 0; + int count = 1; + if (isLeaf(root)) return count; + Object[] children = root.getChildren(); + for (int i=0; i<children.length; i++) + count+=countNodes((XMLNode) children[i]); + return count; + } + + /* returns index of node x in fNLeft */ + protected int indexOfLN (XMLNode x) { + int i= 0; + while ((i<fNLeft.size()) && (fNLeft.elementAt(i) != x)) + i++; + return i; + } + + /* returns index of node y in fNRight */ + protected int indexOfRN (XMLNode y) { + int j= 0; + while ((j<fNRight.size()) && (fNRight.elementAt(j) != y)) + j++; + return j; + } + +/* for testing */ + public Vector getMatches() { + return fMatches; + } + + protected class XMLComparator implements IRangeComparator { + + private Object[] fXML_elements; + + public XMLComparator(Object[] xml_elements) { + fXML_elements= xml_elements; + } + + /* + * @see IRangeComparator#getRangeCount() + */ + public int getRangeCount() { + return fXML_elements.length; + } + + /* + * @see IRangeComparator#rangesEqual(int, IRangeComparator, int) + */ + public boolean rangesEqual( + int thisIndex, + IRangeComparator other_irc, + int otherIndex) { + + if (other_irc instanceof XMLComparator) { + XMLComparator other= (XMLComparator) other_irc; + //return ((XMLNode)fXML_elements[thisIndex]).subtreeEquals(other.getXML_elements()[otherIndex]); + + //ordered compare of subtrees + //boolean result= ((XMLNode)fXML_elements[thisIndex]).subtreeEquals(other.getXML_elements()[otherIndex]); + + //taking ids into account + boolean sameId= false; + XMLNode thisNode= (XMLNode)fXML_elements[thisIndex]; + XMLNode otherNode= (XMLNode)other.getXML_elements()[otherIndex]; + if ( thisNode.usesIDMAP() && otherNode.usesIDMAP() ) { + if ( otherNode.getOrigId().equals(thisNode.getOrigId()) ) { + sameId= true; + } + } + + //unordered compare of subtrees + int distance= dist((XMLNode)other.getXML_elements()[otherIndex] , (XMLNode)fXML_elements[thisIndex]); + return sameId || distance == 0; + } + return false; + } + + /* + * @see IRangeComparator#skipRangeComparison(int, int, IRangeComparator) + */ + public boolean skipRangeComparison( + int length, + int maxLength, + IRangeComparator other) { + return false; + } + + public Object[] getXML_elements() { + return fXML_elements; + } + + } + + /* represents a matching between a node in the Left tree and a node in the Right tree */ + class Match { + public XMLNode fx; + public XMLNode fy; + + Match(XMLNode x, XMLNode y) { + fx = x; + fy = y; + } + + public boolean equals(Object obj) { + if (obj instanceof Match) { + Match m = (Match) obj; + if (m != null) + return fx == m.fx && fy == m.fy; + } + return false; + } + } + + protected int handleRangeDifferencer(Object[] xc_elements, Object[] yc_elements, ArrayList DTMatching, int distance) { + RangeDifference[] differences= RangeDifferencer.findDifferences(new XMLComparator(xc_elements), new XMLComparator(yc_elements)); + + int cur_pos_left= 0; + int cur_pos_right= 0; + for (int i= 0; i < differences.length; i++) { + RangeDifference rd= differences[i]; + int equal_length= rd.leftStart(); + //handle elements before current range which are unchanged + while (cur_pos_left < equal_length) { + //assuming XMLComparator has already filled fDT and fDT_Matchings for subtrees + //rooted at xc_elements[cur_pos_left] and yc_elements[cur_pos_right] +// if ( fDT[indexOfLN( (XMLNode)xc_elements[cur_pos_left])][indexOfRN( (XMLNode)yc_elements[cur_pos_right])] != 0) +// System.out.println("distance not 0"); +// distance += fDT[indexOfLN( (XMLNode)xc_elements[cur_pos_left])][indexOfRN( (XMLNode)yc_elements[cur_pos_right])]; + //DTMatching.addAll(fDT_Matchings[index_left][index_right]); + DTMatching.add(new Match( (XMLNode)xc_elements[cur_pos_left], (XMLNode)yc_elements[cur_pos_right])); + cur_pos_left++; + cur_pos_right++; + } + //now handle RangeDifference rd[i] + int smaller_length, greater_length; + boolean leftGreater= rd.leftLength() > rd.rightLength(); + if (leftGreater) { + smaller_length= rd.rightLength(); + greater_length= rd.leftLength(); + } else { + smaller_length= rd.leftLength(); + greater_length= rd.rightLength(); + } + + //handle elements elements in range + for (int j=0; j < smaller_length; j++) { + distance += dist((XMLNode) xc_elements[cur_pos_left], (XMLNode) yc_elements[cur_pos_right]); + DTMatching.add(new Match( (XMLNode)xc_elements[cur_pos_left], (XMLNode)yc_elements[cur_pos_right])); + cur_pos_left++; + cur_pos_right++; + } + //int cur_pos_greater= (leftGreater)?cur_pos_left:cur_pos_right; + if (leftGreater) { + for (int j=smaller_length; j < greater_length; j++) { + distance += countNodes((XMLNode) xc_elements[cur_pos_left]); + DTMatching.add(new Match( (XMLNode)xc_elements[cur_pos_left], null)); + cur_pos_left++; + } + } else { + for (int j=smaller_length; j < greater_length; j++) { + distance += countNodes((XMLNode) yc_elements[cur_pos_right]); + DTMatching.add(new Match( null, (XMLNode)yc_elements[cur_pos_right])); + cur_pos_right++; + } + } +// for (int j=smaller_length; j < greater_length; j++) { +// distance += countNodes((XMLNode) xc_elements[cur_pos_greater]); +// cur_pos_greater++; +// } +// if (leftGreater) +// cur_pos_left= cur_pos_greater; +// else +// cur_pos_right= cur_pos_greater; + } + + for (int i= cur_pos_left; i < xc_elements.length; i++) { + //distance += fDT[indexOfLN( (XMLNode)xc_elements[cur_pos_left])][indexOfRN( (XMLNode)yc_elements[cur_pos_right])]; + //DTMatching.addAll(fDT_Matchings[index_left][index_right]); + DTMatching.add(new Match( (XMLNode)xc_elements[cur_pos_left], (XMLNode)yc_elements[cur_pos_right])); + cur_pos_left++; + cur_pos_right++; + } + + return distance; + } + + abstract public void match(XMLNode LeftTree, XMLNode RightTree, boolean rightTreeIsAncestor, IProgressMonitor monitor) throws InterruptedException; + + protected int dist(XMLNode x, XMLNode y) { + //System.out.println("dist( "+x.getSignature()+" , "+y.getSignature()+")"); + int ret= NO_ENTRY; + + int index_x= indexOfLN(x); + int index_y= indexOfRN(y); + if (fDT[index_x][index_y] != NO_ENTRY) return fDT[index_x][index_y]; + + if (isLeaf(x) && isLeaf(y)) { + if (x.getXMLType() == XMLStructureCreator.TYPE_ELEMENT) { + if ( x.getSignature().equals(y.getSignature()) ) { + ret= 0; + fDT[index_x][index_y] = ret; + } else { + ret= 2; + fDT[index_x][index_y] = ret; + } + return ret; + } else if (x.getXMLType() == XMLStructureCreator.TYPE_ATTRIBUTE || x.getXMLType() == XMLStructureCreator.TYPE_TEXT) { + if ( x.getSignature().equals(y.getSignature()) ) { + if (x.getValue().equals(y.getValue())) { + ret= 0; + fDT[index_x][index_y] = ret; + } else { + ret= 1; + fDT[index_x][index_y] = ret; + } + } else { + ret= 2; + fDT[index_x][index_y] = ret; + } + return ret; + } + } else {//x or y are not leaves + if ( !x.getSignature().equals(y.getSignature()) ) { + ret= countNodes(x) + countNodes(y); + fDT[index_x][index_y] = ret; + return ret; + } + //x.getSignature().equals(y.getSignature()) + if (isLeaf(x)) { + ret= countNodes(y)-1; + fDT[index_x][index_y] = ret; + return ret; + } + if (isLeaf(y)) { + ret= countNodes(x)-1; + fDT[index_x][index_y] = ret; + return ret; + } + //both x and y have children + return handleXandYnotLeaves(x,y); + } + return ret; + } + + abstract int handleXandYnotLeaves(XMLNode x, XMLNode y); +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/AttributesImpl.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/AttributesImpl.java new file mode 100644 index 0000000000..b62f9a3996 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/AttributesImpl.java @@ -0,0 +1,331 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import org.xml.sax.Attributes; + +/** + * An Attributes implementation that can perform more operations + * than the attribute list helper supplied with the standard SAX2 + * distribution. + */ +public class AttributesImpl implements Attributes { + + /** Head node. */ + private ListNode fHead; + + /** Tail node. */ + private ListNode fTail; + + /** Length. */ + private int fLength; + + + /* Returns the number of attributes. */ + public int getLength() { + return fLength; + } + + /* Returns the index of the specified attribute. */ + public int getIndex(String raw) { + ListNode place= fHead; + int index= 0; + while (place != null) { + if (place.raw.equals(raw)) { + return index; + } + index++; + place= place.next; + } + return -1; + } + + /* Returns the index of the specified attribute. */ + public int getIndex(String uri, String local) { + ListNode place= fHead; + int index= 0; + while (place != null) { + if (place.uri.equals(uri) && place.local.equals(local)) { + return index; + } + index++; + place= place.next; + } + return -1; + } + + /* Returns the attribute URI by index. */ + public String getURI(int index) { + + ListNode node= getListNodeAt(index); + return node != null ? node.uri : null; + } + + /* Returns the attribute local name by index. */ + public String getLocalName(int index) { + + ListNode node= getListNodeAt(index); + return node != null ? node.local : null; + } + + /* Returns the attribute raw name by index. */ + public String getQName(int index) { + + ListNode node= getListNodeAt(index); + return node != null ? node.raw : null; + + } + + /* Returns the attribute type by index. */ + public String getType(int index) { + + ListNode node= getListNodeAt(index); + return (node != null) ? node.type : null; + } + + /* Returns the attribute type by uri and local. */ + public String getType(String uri, String local) { + + ListNode node= getListNode(uri, local); + return (node != null) ? node.type : null; + + } + + /* Returns the attribute type by raw name. */ + public String getType(String raw) { + + ListNode node= getListNode(raw); + return (node != null) ? node.type : null; + } + + /* Returns the attribute value by index. */ + public String getValue(int index) { + + ListNode node= getListNodeAt(index); + return (node != null) ? node.value : null; + } + + /* Returns the attribute value by uri and local. */ + public String getValue(String uri, String local) { + + ListNode node= getListNode(uri, local); + return (node != null) ? node.value : null; + } + + /* Returns the attribute value by raw name. */ + public String getValue(String raw) { + + ListNode node= getListNode(raw); + return (node != null) ? node.value : null; + } + + /* Adds an attribute. */ + public void addAttribute(String raw, String type, String value) { + addAttribute(null, null, raw, type, value); + } + + /* Adds an attribute. */ + public void addAttribute( + String uri, + String local, + String raw, + String type, + String value) { + + ListNode node= new ListNode(uri, local, raw, type, value); + if (fLength == 0) { + fHead= node; + } else { + fTail.next= node; + } + fTail= node; + fLength++; + } + + /* Inserts an attribute. */ + public void insertAttributeAt( + int index, + String raw, + String type, + String value) { + insertAttributeAt(index, null, null, raw, type, value); + } + + /* Inserts an attribute. */ + public void insertAttributeAt( + int index, + String uri, + String local, + String raw, + String type, + String value) { + + // if list is empty, add attribute + if (fLength == 0 || index >= fLength) { + addAttribute(uri, local, raw, type, value); + return; + } + + // insert at beginning of list + ListNode node= new ListNode(uri, local, raw, type, value); + if (index < 1) { + node.next= fHead; + fHead= node; + } else { + ListNode prev= getListNodeAt(index - 1); + node.next= prev.next; + prev.next= node; + } + fLength++; + } + + /* Removes an attribute. */ + public void removeAttributeAt(int index) { + + if (fLength == 0) + return; + + if (index == 0) { + fHead= fHead.next; + if (fHead == null) { + fTail= null; + } + fLength--; + } else { + ListNode prev= getListNodeAt(index - 1); + ListNode node= getListNodeAt(index); + if (node != null) { + prev.next= node.next; + if (node == fTail) { + fTail= prev; + } + fLength--; + } + } + } + + /* Removes the specified attribute. */ + public void removeAttribute(String raw) { + removeAttributeAt(getIndex(raw)); + } + + /* Removes the specified attribute. */ + public void removeAttribute(String uri, String local) { + removeAttributeAt(getIndex(uri, local)); + } + + /* Returns the node at the specified index. */ + private ListNode getListNodeAt(int i) { + + for (ListNode place= fHead; place != null; place= place.next) { + if (--i == -1) { + return place; + } + } + return null; + } + + /* Returns the first node with the specified uri and local. */ + public ListNode getListNode(String uri, String local) { + + if (uri != null && local != null) { + ListNode place= fHead; + while (place != null) { + if (place.uri != null + && place.local != null + && place.uri.equals(uri) + && place.local.equals(local)) { + return place; + } + place= place.next; + } + } + return null; + } + + /* Returns the first node with the specified raw name. */ + private ListNode getListNode(String raw) { + + if (raw != null) { + for (ListNode place= fHead; place != null; place= place.next) { + if (place.raw != null && place.raw.equals(raw)) { + return place; + } + } + } + + return null; + } + + /* Returns a string representation of this object. */ + public String toString() { + StringBuffer str= new StringBuffer(); + + str.append('['); + str.append("len="); //$NON-NLS-1$ + str.append(fLength); + str.append(", {"); //$NON-NLS-1$ + for (ListNode place= fHead; place != null; place= place.next) { + str.append(place.toString()); + if (place.next != null) { + str.append(", "); //$NON-NLS-1$ + } + } + str.append("}]"); //$NON-NLS-1$ + + return str.toString(); + } + + /* + * An attribute node. + */ + static class ListNode { + + /** Attribute uri. */ + public String uri; + + /** Attribute local. */ + public String local; + + /** Attribute raw. */ + public String raw; + + /** Attribute type. */ + public String type; + + /** Attribute value. */ + public String value; + + /** Next node. */ + public ListNode next; + + /* Constructs a list node. */ + public ListNode( + String uri0, + String local0, + String raw0, + String type0, + String value0) { + + this.uri= uri0; + this.local= local0; + this.raw= raw0; + this.type= type0; + this.value= value0; + + } + + /* Returns string representation of this object. */ + public String toString() { + return raw != null ? raw : local; + } + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/OrderedMatching.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/OrderedMatching.java new file mode 100644 index 0000000000..9d960aed9f --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/OrderedMatching.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Vector; + +import org.eclipse.core.runtime.IProgressMonitor; + +public class OrderedMatching extends AbstractMatching { + + public OrderedMatching() { + super(); + } + + protected int orderedMath(XMLNode x, XMLNode y) { + //assumes x and y have children + Object[] xc = x.getChildren(); + Object[] yc = y.getChildren(); + + ArrayList xc_elementsAL = new ArrayList(); + ArrayList xc_attrsAL = new ArrayList(); + + ArrayList yc_elementsAL = new ArrayList(); + ArrayList yc_attrsAL = new ArrayList(); + + //find attributes and elements and put them in xc_elementsAL and xc_attrsAL, respectively + for (int i = 0; i < xc.length; i++) { + XMLNode x_i = (XMLNode) xc[i]; + if (x_i.getXMLType().equals(XMLStructureCreator.TYPE_ELEMENT)) { + xc_elementsAL.add(x_i); + } else if ( + x_i.getXMLType().equals(XMLStructureCreator.TYPE_ATTRIBUTE)) { + xc_attrsAL.add(x_i); + } + } + + //do the same for yc + for (int i = 0; i < yc.length; i++) { + XMLNode y_i = (XMLNode) yc[i]; + if (y_i.getXMLType().equals(XMLStructureCreator.TYPE_ELEMENT)) { + yc_elementsAL.add(y_i); + } else if ( + y_i.getXMLType().equals(XMLStructureCreator.TYPE_ATTRIBUTE)) { + yc_attrsAL.add(y_i); + } + } + + Object[] xc_elements = xc_elementsAL.toArray(); + Object[] yc_elements = yc_elementsAL.toArray(); + + ArrayList DTMatching = new ArrayList(); + // Matching to be added to Entry in fDT_Matchings + int distance = 0; //distance to be added to entry in fDT + + // perform unordered matching on attributes + // this updates fDT and fDT_Matchings + if (xc_attrsAL.size() > 0 || yc_attrsAL.size() > 0) { + if (xc_attrsAL.size() == 0) + distance += yc_attrsAL.size(); + else if (yc_attrsAL.size() == 0) + distance += xc_attrsAL.size(); + else { + distance = handleAttributes(xc_attrsAL, yc_attrsAL, DTMatching); + } + } + + distance = handleRangeDifferencer( + xc_elements, + yc_elements, + DTMatching, + distance); + + fDT[indexOfLN(x)][indexOfRN(y)]= distance; + fDT_Matchings[indexOfLN(x)][indexOfRN(y)]= DTMatching; + return distance; + + } + + /* matches two trees according to paper "X-Diff", p. 16 */ + public void match(XMLNode LeftTree, XMLNode RightTree, boolean rightTreeIsAncestor, + IProgressMonitor monitor) throws InterruptedException { + + fNLeft = new Vector(); + //numbering LeftTree: Mapping nodes in LeftTree to numbers to be used as array indexes + fNRight = new Vector(); + //numbering RightTree: Mapping nodes in RightTree to numbers to be used as array indexes + numberNodes(LeftTree, fNLeft); + numberNodes(RightTree, fNRight); + fDT = new int[fNLeft.size()][fNRight.size()]; + fDT_Matchings = new ArrayList[fNLeft.size()][fNRight.size()]; + for (int i = 0; i < fDT.length; i++) { + fDT[i] = new int[fNRight.size()]; + for (int j = 0; j < fDT[0].length; j++) { + fDT[i][j] = NO_ENTRY; + } + } + + dist(LeftTree, RightTree); + // /* mark matchings on LeftTree and RightTree */ + fMatches = new Vector(); + if (!LeftTree.getSignature().equals(RightTree.getSignature())) { + //matching is empty + } else { + fMatches.add(new Match(LeftTree, RightTree)); + for (int i_M = 0; i_M < fMatches.size(); i_M++) { + Match m = (Match) fMatches.elementAt(i_M); + if (!isLeaf(m.fx) && !isLeaf(m.fy)) { + if (fDT_Matchings[indexOfLN(m.fx)][indexOfRN(m.fy)] != null) + fMatches.addAll(fDT_Matchings[indexOfLN(m.fx)][indexOfRN(m.fy)]); + } + } + } + //end of Step2 + /* Renumber Id of Nodes to follow Matches. Or for ancestor, copy over Id to ancestor */ + if (rightTreeIsAncestor) { + for (ListIterator it_M = fMatches.listIterator(); it_M.hasNext();) { + Match m = (Match) it_M.next(); + if (m.fx != null && m.fy != null) + m.fy.setId(m.fx.getId()); + } + } else { + int newId = 0; + for (ListIterator it_M = fMatches.listIterator(); it_M.hasNext(); newId++) { + Match m = (Match) it_M.next(); + if (m.fx != null) + m.fx.setId(Integer.toString(newId)); + if (m.fy != null) + m.fy.setId(Integer.toString(newId)); + } + } + } + + public int handleAttributes(ArrayList xc_attrs, ArrayList yc_attrs, ArrayList DTMatching) { + int distance = 0; + x_for : for ( + Iterator iter_xc = xc_attrs.iterator(); iter_xc.hasNext();) { + XMLNode x_attr = (XMLNode) iter_xc.next(); + String x_attr_name = x_attr.getName(); + for (Iterator iter_yc = yc_attrs.iterator(); iter_yc.hasNext();) { + XMLNode y_attr = (XMLNode) iter_yc.next(); + if (y_attr.getName().equals(x_attr_name)) { + if (!y_attr.getValue().equals(x_attr.getValue())) + distance += 1; + DTMatching.add(new Match(x_attr, y_attr)); + yc_attrs.remove(y_attr); + continue x_for; + } + } + DTMatching.add(new Match(x_attr, null)); + distance += 1; + } + + for (Iterator iter_yc = yc_attrs.iterator(); iter_yc.hasNext();) { + DTMatching.add(new Match(null, (XMLNode) iter_yc.next())); + distance += 1; + } + + return distance; + } + + protected int handleXandYnotLeaves(XMLNode x, XMLNode y) { + /* handle entries as ordered*/ + return orderedMath(x, y); + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLChildren.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLChildren.java new file mode 100644 index 0000000000..033f8c8de5 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLChildren.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import java.util.HashMap; + +import org.eclipse.jface.text.IDocument; + +/** XMLNode that has children elements */ +public class XMLChildren extends XMLNode { + + public int children; // counts the number of children + public HashMap childElements; // maps the name of XML child elements to their # of occurence + + public XMLChildren(String XMLType, String id, String value, String signature, IDocument doc, int start, int length) { + super(XMLType, id, value, signature, doc, start, length); + children= 0; + childElements = new HashMap(); + } +} + diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLContentMergeViewer.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLContentMergeViewer.java new file mode 100644 index 0000000000..bd08a2f301 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLContentMergeViewer.java @@ -0,0 +1,38 @@ +package org.eclipse.pde.internal.ui.compare; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.contentmergeviewer.TextMergeViewer; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.jface.text.TextViewer; +import org.eclipse.jface.text.rules.FastPartitioner; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.pde.internal.ui.editor.text.ColorManager; +import org.eclipse.pde.internal.ui.editor.text.IColorManager; +import org.eclipse.pde.internal.ui.editor.text.XMLConfiguration; +import org.eclipse.pde.internal.ui.editor.text.XMLPartitionScanner; +import org.eclipse.swt.widgets.Composite; + +public class XMLContentMergeViewer extends TextMergeViewer { + + public XMLContentMergeViewer(Composite parent, CompareConfiguration config) { + super(parent, config); + } + + protected void configureTextViewer(TextViewer textViewer) { + if (textViewer instanceof SourceViewer) { + IColorManager colorManager = ColorManager.getDefault(); + ((SourceViewer)textViewer).configure(new XMLConfiguration(colorManager)); + ((SourceViewer)textViewer).getTextWidget().setFont(JFaceResources.getTextFont()); + } + } + + protected IDocumentPartitioner getDocumentPartitioner() { + return new FastPartitioner(new XMLPartitionScanner(), XMLPartitionScanner.PARTITIONS); + } + + public String getTitle() { + return XMLStructureCreator.DEFAULT_NAME; + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLContentMergeViewerCreator.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLContentMergeViewerCreator.java new file mode 100644 index 0000000000..0bd771dfa1 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLContentMergeViewerCreator.java @@ -0,0 +1,13 @@ +package org.eclipse.pde.internal.ui.compare; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.IViewerCreator; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Composite; + +public class XMLContentMergeViewerCreator implements IViewerCreator { + + public Viewer createViewer(Composite parent, CompareConfiguration config) { + return new XMLContentMergeViewer(parent, config); + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLNode.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLNode.java new file mode 100644 index 0000000000..d15b914475 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLNode.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import org.eclipse.compare.CompareUI; +import org.eclipse.compare.ITypedElement; +import org.eclipse.compare.structuremergeviewer.DocumentRangeNode; +import org.eclipse.jface.text.IDocument; +import org.eclipse.swt.graphics.Image; + +/** + * Objects that make up the parse tree. + */ +public class XMLNode extends DocumentRangeNode implements ITypedElement { + + private String fValue; + private String fName; + private String fSignature; + private String fOrigId; + private XMLNode parent; + private String fXMLType; + private boolean fUsesIDMAP; + + public int bodies; // counts the number of bodies + + public XMLNode(String XMLType, String id, String value, String signature, IDocument doc, int start, int length) { + super(0, id, doc, start, length); + fXMLType= XMLType; + fValue= value; + fSignature= signature; + fOrigId= id; + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Created XMLNode with XMLType: " + XMLType + ", id: " + id + ", value: " + value + ", signature: " + fSignature); //$NON-NLS-1$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ + bodies= 0; + fUsesIDMAP= false; + } + + void setValue(String value) { + fValue= value; + } + + String getValue() { + return fValue; + } + + /* + * @see ITypedElement#getName + */ + public String getName() { + if (fName != null) + return fName; + return this.getId(); + } + + public void setName(String name) { + fName= name; + } + + /* + * Every xml node is of type "txt" so that the builtin TextMergeViewer is used automatically. + * @see ITypedElement#getType + */ + public String getType() { + return "txt"; //$NON-NLS-1$ + } + + + /* + * @see ITypedElement#getImage + */ + public Image getImage() { + return CompareUI.getImage(XMLStructureMapping.getImageKey(getXMLType())); + } + + public void setParent(XMLNode parent0) { + this.parent= parent0; + } + + public XMLNode getParent() { + return this.parent; + } + + String getXMLType() { + return fXMLType; + } + + String getSignature() { + return fSignature; + } + + void setOrigId(String id) { + fOrigId= id; + } + + public String getOrigId() { + return fOrigId; + } + + public void setUsesIDMAP(boolean b) { + fUsesIDMAP= b; + } + + public boolean usesIDMAP() { + return fUsesIDMAP; + } + + //for tests + public boolean testEquals(Object obj) { + if (obj instanceof XMLNode) { + XMLNode n= (XMLNode) obj; + return fValue.equals(n.getValue()) + && fSignature.equals(n.getSignature()) + && fXMLType.equals(n.getXMLType()) + && fUsesIDMAP == n.usesIDMAP(); + } + return false; + } + + /* + * Returns true if the subtree rooted at this node is equals to the subtree rooted at <code>obj</code> + */ + public boolean subtreeEquals(Object obj) { + if (!testEquals(obj)) + return false; + if (obj instanceof XMLNode) { + XMLNode n= (XMLNode) obj; + if (getXMLType().equals(XMLStructureCreator.TYPE_ATTRIBUTE) + && n.getXMLType().equals(XMLStructureCreator.TYPE_ATTRIBUTE)) + return true; + Object[] children= getChildren(); + Object[] n_children= n.getChildren(); + //if both nodes have no children, return true; + if ((children == null || children.length <= 0) + && (n_children == null || n_children.length <= 0)) + return true; + //now at least one of the two nodes has children; + /* so if one of the two nodes has no children, or they don't have the same number of children, + * return false; + */ + if ((children == null || children.length <= 0) + || (n_children == null || n_children.length <= 0) + || (children.length != n_children.length)) + return false; + //now both have children and the same number of children + for (int i= 0; i < children.length; i++) { + /* if the subtree rooted at children[i] is not equal to the subtree rooted at n_children[i], + * return false + */ + if (!((XMLNode) children[i]).subtreeEquals(n_children[i])) + return false; + } + } + return true; + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureCreator.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureCreator.java new file mode 100644 index 0000000000..822904b33a --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureCreator.java @@ -0,0 +1,697 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; +import java.util.HashMap; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.eclipse.compare.IEditableContent; +import org.eclipse.compare.IEncodedStreamContentAccessor; +import org.eclipse.compare.IStreamContentAccessor; +import org.eclipse.compare.structuremergeviewer.IStructureComparator; +import org.eclipse.compare.structuremergeviewer.IStructureCreator; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.internal.ui.PDEPlugin; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.helpers.LocatorImpl; + +/** + * This structure analyzer builds a parse tree of an XML document found in a + * <code>IByteContentAccessor</code> input by calling getStructure(Object) + */ +public class XMLStructureCreator implements IStructureCreator { + + protected static final boolean DEBUG_MODE = true; + public static final String DEFAULT_NAME = "XML Compare"; + public static final String USE_UNORDERED = "Unordered"; + public static final String USE_ORDERED = "Ordered"; + + public static final String DEFAULT_IDMAP = XMLStructureMapping.ECLIPSE_PLUGIN; + public static final String TYPE_ROOT = "plugin"; //$NON-NLS-1$ + public static final String TYPE_EXTENSION = "extension"; //$NON-NLS-1$ + public static final String TYPE_EXTENSIONPOINT = "extension-point"; //$NON-NLS-1$ + public static final String TYPE_ELEMENT = "element"; //$NON-NLS-1$ + public static final String TYPE_TEXT = "text"; //$NON-NLS-1$ + public static final String TYPE_ATTRIBUTE = "attribute"; //$NON-NLS-1$ + + // for signatures + public static final String ROOT_ID = "root"; //$NON-NLS-1$ + public static final char SIGN_SEPARATOR = '>';//'.' + public static final char SIGN_ENCLOSING = '$'; + public static final String SIGN_ELEMENT = SIGN_ENCLOSING + TYPE_ELEMENT + SIGN_ENCLOSING; + public static final String SIGN_TEXT = SIGN_ENCLOSING + TYPE_TEXT + SIGN_ENCLOSING; + public static final String SIGN_ATTRIBUTE = SIGN_ENCLOSING + TYPE_ATTRIBUTE + SIGN_ENCLOSING; + public static final char ID_SEPARATOR = '<'; + public static final char ID_TYPE_BODY = '<'; + + private XMLNode fCurrentParent; + private String fSignature; + private Document fDoc; + private boolean ignoreBodies = false; + private HashMap fIdMapsInternal; + private HashMap idMap; + private String fIdMapToUse; + private boolean fRemoveWhiteSpace; + + protected class XMLHandler extends DefaultHandler { + + protected Locator prevlocator; //previous locator + protected Locator locator; //current locator + + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + public void processingInstruction(String target, String data) { + prevlocator = new LocatorImpl(locator); + } + + public void startDocument() { + prevlocator = new LocatorImpl(locator); + } + + public void startElement(String uri, String local, String raw, Attributes attrs) { + /* add root node for this element */ + if (XMLStructureCreator.DEBUG_MODE) { + if (locator != null && prevlocator != null) { + System.out.println("prevlocator: line " + prevlocator.getLineNumber() + " column " + prevlocator.getColumnNumber() + " id " + prevlocator.getPublicId()); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + System.out.println("locator: line " + locator.getLineNumber() + " column " + locator.getColumnNumber() + " id " + locator.getPublicId()); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + } + } + + try { + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Node where children field accessed: " + fCurrentParent.getId()); //$NON-NLS-1$ + XMLChildren currentParent = (XMLChildren) fCurrentParent; + currentParent.children++; + String elementId; + String elementName; + IRegion r = fDoc.getLineInformation(prevlocator.getLineNumber() - 1); + + fSignature = fSignature + raw + SIGN_SEPARATOR; + + if (idMap.containsKey(fSignature)) { + String attrName = (String) idMap.get(fSignature); + String attrValue = attrs.getValue(attrName); + elementId = raw + new Character(ID_SEPARATOR) + attrValue; + elementName = raw; + if (attrValue != null) + elementName += " [" + attrName + "=" + attrs.getValue(attrName) + "]"; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + } else { + if (!currentParent.childElements.containsKey(raw)) { + currentParent.childElements.put(raw, new Integer(1)); + } else { + currentParent.childElements.put(raw, new Integer(((Integer) currentParent.childElements.get(raw)).intValue() + 1)); + } + elementId = raw + new Character(ID_SEPARATOR) + "[" + currentParent.childElements.get(raw) + "]"; //$NON-NLS-2$ //$NON-NLS-1$ + elementName = MessageFormat.format("{0} [{1}]", new String[] { raw, currentParent.childElements.get(raw).toString() }); //$NON-NLS-1$ + } + int start = r.getOffset() + prevlocator.getColumnNumber() - 1; + if (start < 0) + start = 0; + String type = TYPE_ELEMENT; + if (currentParent.getId() == ROOT_ID && elementName.startsWith(TYPE_ROOT)) + type = TYPE_ROOT; + else if (currentParent.getXMLType().equals(TYPE_ROOT)) { + if (elementName.startsWith(TYPE_EXTENSIONPOINT)) + type = TYPE_EXTENSIONPOINT; + else if (elementName.startsWith(TYPE_EXTENSION)) + type = TYPE_EXTENSION; + } + XMLNode currentElement = new XMLChildren(type, elementId, elementId, + (fSignature + SIGN_ELEMENT), fDoc, start, 0); + currentElement.setName(elementName); + if (idMap.containsKey(fSignature)) + currentElement.setUsesIDMAP(true); + + fCurrentParent.addChild(currentElement); + currentElement.setParent(fCurrentParent); + fCurrentParent = currentElement; + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("\nAdded Element " + raw + " with offset " + r.getOffset()); //$NON-NLS-2$ //$NON-NLS-1$ + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("fcurrentParent1: " + fCurrentParent.getId()); //$NON-NLS-1$ + + if (attrs != null) { + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("attrs != null, fcurrentParent is " + fCurrentParent.getId()); //$NON-NLS-1$ + int len = attrs.getLength(); + int element_lines_length_size; + int[] element_lines_length; + int column_offset; + String element_string; + if (fCurrentParent.getParent().getId().equals(ROOT_ID)) { + element_lines_length_size = locator.getLineNumber() - prevlocator.getLineNumber(); + element_lines_length = new int[element_lines_length_size]; + column_offset = 0; + element_string = ""; //$NON-NLS-1$ + for (int i_ell = 0; i_ell < element_lines_length.length; i_ell++) { + IRegion attr_r = fDoc.getLineInformation(i_ell + prevlocator.getLineNumber()); + element_lines_length[i_ell] = fDoc.get(attr_r.getOffset(), attr_r.getLength()).length() + 1; + element_string = element_string + fDoc.get(attr_r.getOffset(), attr_r.getLength()) + " "; //$NON-NLS-1$ + } + } else { + element_lines_length_size = locator.getLineNumber() - prevlocator.getLineNumber() + 1; + //if (element_lines_length_size < 1) + // element_lines_length_size = 1; + element_lines_length = new int[element_lines_length_size]; + IRegion first_line = fDoc.getLineInformation(prevlocator.getLineNumber() - 1); + column_offset = prevlocator.getColumnNumber() - 1; + int first_line_relevant_offset = first_line.getOffset() + column_offset; + int first_line_relevant_length = first_line.getLength() - column_offset; + element_string = fDoc.get(first_line_relevant_offset, first_line_relevant_length) + " "; //$NON-NLS-1$ + element_lines_length[0] = element_string.length(); + for (int i_ell = 1; i_ell < element_lines_length.length; i_ell++) { + IRegion attr_r = fDoc.getLineInformation(i_ell + prevlocator.getLineNumber() - 1); + element_lines_length[i_ell] = fDoc.get(attr_r.getOffset(), attr_r.getLength()).length() + 1; + element_string = element_string + fDoc.get(attr_r.getOffset(), attr_r.getLength()) + " "; //$NON-NLS-1$ + } + } + + for (int i_attr = 0; i_attr < len; i_attr++) { + String attr_name = attrs.getQName(i_attr); + String attr_value = attrs.getValue(i_attr); + + /* + * find range of attribute in doc; manually parses the + * line + */ + boolean found = false; + int first_quotes = -1; + int second_quotes = -1; + int id_index = -1; + while (!found) { + first_quotes = element_string.indexOf("\"", second_quotes + 1); //$NON-NLS-1$ + second_quotes = element_string.indexOf("\"", first_quotes + 1); //$NON-NLS-1$ + String value; + try { + value = element_string.substring(first_quotes + 1, second_quotes); + } catch (Exception e) { + value = ""; //$NON-NLS-1$ + } + if (value.equals("")) //$NON-NLS-1$ + found = true; + else if (value.equals(attr_value)) { + id_index = element_string.lastIndexOf(attr_name, first_quotes - 1); + boolean wrong = false; + boolean found_equal = false; + for (int i_char = id_index + attr_name.length(); i_char < first_quotes && !wrong; i_char++) { + if (element_string.charAt(i_char) == '=') + if (!found_equal) + found_equal = true; + else + wrong = true; + else if (!Character.isWhitespace(element_string.charAt(i_char))) + wrong = true; + } + if (!wrong) + found = true; + } + } + //id_index has one char missing for every line (the + // final cr) + int line_of_index = 0; + for (line_of_index = 0; id_index > element_lines_length[line_of_index] - 1; line_of_index++) + id_index -= (element_lines_length[line_of_index]); + if (line_of_index == 0) + id_index += column_offset; + if (fCurrentParent.getParent().getId().equals(ROOT_ID)) + line_of_index += prevlocator.getLineNumber(); + else + line_of_index += prevlocator.getLineNumber() - 1; + //index at line line_of_index, line offset id_index + int line_of_end_of_value = 0; + int end_of_value_index = second_quotes; + for (line_of_end_of_value = 0; end_of_value_index > element_lines_length[line_of_end_of_value] - 1; line_of_end_of_value++) + end_of_value_index -= (element_lines_length[line_of_end_of_value]); + if (line_of_end_of_value == 0) + end_of_value_index += column_offset; + if (fCurrentParent.getParent().getId().equals(ROOT_ID)) + line_of_end_of_value += prevlocator.getLineNumber(); + else + line_of_end_of_value += prevlocator.getLineNumber() - 1; + //end of value at line line_of_end_of_value, line + // offset end_of_value_index + + int attr_start_doc_offset = fDoc.getLineInformation(line_of_index).getOffset() + id_index; + //int attr_length_doc_offset = + // fdoc.getLineInformation(line_of_value).getOffset()+value_index+attr_value.length()+1+(line_of_end_of_value-line_of_index) + // - attr_start_doc_offset; + int attr_length_doc_offset = fDoc.getLineInformation(line_of_end_of_value).getOffset() + end_of_value_index + 1 - attr_start_doc_offset; + currentElement = new XMLNode(TYPE_ATTRIBUTE, attr_name, attr_value, (fSignature + attr_name + SIGN_SEPARATOR + SIGN_ATTRIBUTE), fDoc, attr_start_doc_offset, attr_length_doc_offset); + currentElement.setName(attr_name); + fCurrentParent.addChild(currentElement); + currentElement.setParent(fCurrentParent); + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("added attribute " + currentElement.getId() + " with value >" + currentElement.getValue() + "<" + " to element " + fCurrentParent.getId() + " which has parent " + fCurrentParent.getParent().getId()); //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + } + } + } catch (BadLocationException ex) { + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("BadLocationException in startElement(...) " + ex); //$NON-NLS-1$ + new XMLChildren(TYPE_ELEMENT, raw+ "_(" + ((XMLChildren) fCurrentParent).children + ")", raw + "_(" + ((XMLChildren) fCurrentParent).children + ")", (fSignature + SIGN_ELEMENT), fDoc, 0, 0); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + } + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("At the end of startElement(...), fcurrentParent is " + fCurrentParent.getId()); //$NON-NLS-1$ + prevlocator = new LocatorImpl(locator); + } + + public void characters(char ch[], int start, int length) { + if (!ignoreBodies) { + String chars = new String(ch, start, length); + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("characters: >" + chars + "<"); //$NON-NLS-2$ //$NON-NLS-1$ + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Body Location: line " + locator.getLineNumber() + " column " + locator.getColumnNumber()); //$NON-NLS-2$ //$NON-NLS-1$ + + //if text contains only white space, it will be ignored. + if (!trimWhiteSpace(chars).equals("")) { //$NON-NLS-1$ + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Adding body"); //$NON-NLS-1$ + try { + IRegion r = fDoc.getLineInformation(locator.getLineNumber() - 1); + //location returns the END of the characters + //offset of BEGINNING of characters: + int offset = r.getOffset() + locator.getColumnNumber() - 1 - length; + fCurrentParent.bodies++; + String body_value = new String(ch, start, length); + if (fRemoveWhiteSpace) { + body_value = removeWhiteSpace(body_value); + } + XMLNode bodynode = new XMLNode(TYPE_TEXT, "body_(" + fCurrentParent.bodies + ")", body_value, (fSignature + SIGN_TEXT), fDoc, offset, length); //$NON-NLS-2$ //$NON-NLS-1$ + bodynode.setName(NLS.bind("{0} ({1})", "body", Integer.toString(fCurrentParent.bodies))); //$NON-NLS-1$ + fCurrentParent.addChild(bodynode); + bodynode.setParent(fCurrentParent); + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Created body " + fCurrentParent.bodies //$NON-NLS-1$ + + " with offset " + offset + " and length " + length //$NON-NLS-2$ //$NON-NLS-1$ + + " with parent " + bodynode.getParent().getId()); //$NON-NLS-1$ + //bodies as id attributes + String popsig = fCurrentParent.getParent().getSignature(); //signature of parent of + // parent + popsig = popsig.substring(0, popsig.lastIndexOf(SIGN_ELEMENT)); + if (fCurrentParent.bodies == 1 && idMap.containsKey(popsig)) { + String pid = fCurrentParent.getId();//id of parent + String pelementname = pid.substring(0, pid.indexOf("<")); //name of parent element //$NON-NLS-1$ + if (((String) idMap.get(popsig)).equals(ID_TYPE_BODY + pelementname)) { + XMLNode pop = fCurrentParent.getParent(); + String popid = pop.getId(); + String popelementname = popid.substring(0, popid.indexOf("<")); //$NON-NLS-1$ + pop.setId(popelementname + "<" + body_value); //$NON-NLS-1$ + pop.setOrigId(popelementname + "<" + body_value); //$NON-NLS-1$ + pop.setName(MessageFormat.format("{0} [{1}={2}]", new String[] { popelementname, pelementname, body_value })); //$NON-NLS-1$ + pop.setUsesIDMAP(true); + } + } + } catch (BadLocationException ex) { + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("BadLocationException in characters(...) " + ex); //$NON-NLS-1$ + fCurrentParent.addChild(new XMLNode(TYPE_TEXT,"body_(" + fCurrentParent.bodies + ")", new String(ch, start, length), (fSignature + SIGN_TEXT), fDoc, 0, 0)); //$NON-NLS-2$ //$NON-NLS-1$ + } + } + } + prevlocator = new LocatorImpl(locator); + } + + public void ignorableWhitespace(char ch[], int start, int length) { + prevlocator = new LocatorImpl(locator); + } + + public void endElement(String uri, String local, String raw) { + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("\nExiting element " + fCurrentParent.getId()); //$NON-NLS-1$ + + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("prevlocator: line " + prevlocator.getLineNumber() + " column " + prevlocator.getColumnNumber() + " id " + prevlocator.getPublicId()); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("locator: line " + locator.getLineNumber() + " column " + locator.getColumnNumber() + " id " + locator.getPublicId()); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + + if (fCurrentParent.getParent() != null) { + try { + IRegion r2 = fDoc.getLineInformation(locator.getLineNumber() - 1); + Position pos = fCurrentParent.getRange(); + + int elem_length = r2.getOffset() + locator.getColumnNumber() - 1 - pos.getOffset();//length of element from + // start tag to end tag + fCurrentParent.setLength(elem_length); + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("pos.getOffset: " + pos.getOffset() + " elem_length: " + elem_length); //$NON-NLS-2$ //$NON-NLS-1$ + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("fdoc.get(pos.getOffset()+elem_length-5,4): >" + fDoc.get(pos.getOffset() + elem_length - 5, 4) + "<"); //$NON-NLS-2$ //$NON-NLS-1$ + + try { + fCurrentParent.setValue(fDoc.get(pos.getOffset(), elem_length)); + } catch (BadLocationException ex) { + try { + fCurrentParent.setValue(fDoc.get(pos.getOffset(), elem_length - 1)); + } catch (BadLocationException ex2) { + if (XMLStructureCreator.DEBUG_MODE) { + System.out.println("BadLocationException in endElement(...) while attempting fcurrentParent.setValue(...): " + ex); //$NON-NLS-1$ + System.out.println("Attempt to correct BadLocationException failed: " + ex2); //$NON-NLS-1$ + } + } + } + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Value of " + fCurrentParent.getId() + " is >" + fCurrentParent.getValue() + "<"); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + + //going from ending element to parent element + fCurrentParent = fCurrentParent.getParent(); + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("fcurrentParent = fcurrentParent.getParent();"); //$NON-NLS-1$ + } catch (BadLocationException ex) { + if (XMLStructureCreator.DEBUG_MODE) { + System.out.println("BadLocationException in endElement(...): " + ex); //$NON-NLS-1$ + System.out.println("fcurrentParent.getId(): " + fCurrentParent.getId()); //$NON-NLS-1$ + } + } + } else { + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Error: Cannot reach Parent of Parent"); //$NON-NLS-1$ + } + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("fcurrentParent is now " + fCurrentParent.getId()); //$NON-NLS-1$ + + prevlocator = new LocatorImpl(locator); + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Signature before cutting: " + fSignature); //$NON-NLS-1$ + int ssi = fSignature.lastIndexOf(SIGN_SEPARATOR); + // second-last + // separator index + ssi = fSignature.lastIndexOf(SIGN_SEPARATOR, ssi - 1); + + fSignature = fSignature.substring(0, ssi + 1); + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Signature after cutting: " + fSignature); //$NON-NLS-1$ + } + + // + // ErrorHandler methods + // + + /* Warning. */ + public void warning(SAXParseException ex) { + System.err.println("[Warning] " + //$NON-NLS-1$ + getLocationString(ex) + ": " + //$NON-NLS-1$ + ex.getMessage()); + } + + /* Error. */ + public void error(SAXParseException ex) { + System.err.println("[Error] " + //$NON-NLS-1$ + getLocationString(ex) + ": " + //$NON-NLS-1$ + ex.getMessage()); + } + + /* Fatal error. */ + public void fatalError(SAXParseException ex) throws SAXException { + System.err.println("[Fatal Error] " + //$NON-NLS-1$ + getLocationString(ex) + ": " + //$NON-NLS-1$ + ex.getMessage()); + //System.out.println(ex); + //throw ex; + } + + /* Returns a string of the location. */ + private String getLocationString(SAXParseException ex) { + StringBuffer str = new StringBuffer(); + + String systemId = ex.getSystemId(); + if (systemId != null) { + int index = systemId.lastIndexOf('/'); + if (index != -1) + systemId = systemId.substring(index + 1); + str.append(systemId); + } + str.append(':'); + str.append(ex.getLineNumber()); + str.append(':'); + str.append(ex.getColumnNumber()); + + return str.toString(); + + } + } + + public XMLStructureCreator() { + // set default idmap + fIdMapToUse = DEFAULT_IDMAP; + fIdMapsInternal = XMLStructureMapping.getMappings(); + fRemoveWhiteSpace = false; + } + + /* + * This title will be shown in the title bar of the structure compare pane. + */ + public String getName() { + return DEFAULT_NAME; + } + + /* + * Set File extension of the parsed file. This extension will be used to choose an Id Map scheme. + */ + public void setFileExtension(String ext) { + if (ext.equals(".xml")) + setIdMap(XMLStructureMapping.ECLIPSE_PLUGIN); + else if (ext.equals(".exsd")) + setIdMap(XMLStructureMapping.ECLIPSE_SCHEMA); + } + + /** + * Initialize the Id Mappings for the Id Mapping Scheme and the Ordered Elements + * This method must be called before getStructure(Object) is called on the two/three inputs of the compare + */ + public void initIdMaps() { + if (fIdMapsInternal.containsKey(fIdMapToUse)) { + idMap = (HashMap) fIdMapsInternal.get(fIdMapToUse); + } else { + idMap = new HashMap(); + } + + } + + /* + * Returns the XML parse tree of the input. + */ + public IStructureComparator getStructure(Object input) { + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("Starting parse"); //$NON-NLS-1$ + + if (!(input instanceof IStreamContentAccessor)) + return null; + + IStreamContentAccessor sca = (IStreamContentAccessor) input; + + try { + String contents = readString(sca); + if (contents == null) + contents = ""; //$NON-NLS-1$ + fDoc = new Document(contents); + + fSignature = ROOT_ID + SIGN_SEPARATOR; + XMLChildren root = new XMLChildren(TYPE_ELEMENT, ROOT_ID, "", (fSignature + SIGN_ELEMENT), fDoc, 0, fDoc.getLength()); //$NON-NLS-1$ + fCurrentParent = root; + + XMLHandler handler = new XMLHandler(); + + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + SAXParser parser = factory.newSAXParser(); + parser.parse(new InputSource(new StringReader(contents)), handler); + + if (XMLStructureCreator.DEBUG_MODE) + System.out.println("End of parse"); //$NON-NLS-1$ + } catch (SAXParseException e) { + PDEPlugin.log(e); + return null; + } catch (Exception e) { + PDEPlugin.log(e); + return null; + } + return root; + } catch (CoreException ex) { + PDEPlugin.log(ex); + } + return null; + } + + public void save(IStructureComparator structure, Object input) { + if (input instanceof IEditableContent && structure instanceof XMLNode) { + IDocument document = ((XMLNode) structure).getDocument(); + IEditableContent bca = (IEditableContent) input; + String contents = document.get(); + String encoding = null; + if (input instanceof IEncodedStreamContentAccessor) { + try { + encoding = ((IEncodedStreamContentAccessor) input).getCharset(); + } catch (CoreException e) { + // ignore + } + } + if (encoding == null) + encoding = "UTF-8"; //$NON-NLS-1$ + try { + bca.setContent(contents.getBytes(encoding)); + } catch (UnsupportedEncodingException e) { + bca.setContent(contents.getBytes()); + } + } + } + + public String getContents(Object node, boolean ignoreWhitespace) { + if (node instanceof XMLNode) { + String s = ((XMLNode) node).getValue(); + if (ignoreWhitespace) + s = s.trim(); + return s; + } + return null; + } + + public IStructureComparator locate(Object path, Object source) { + return null; + } + + static String readString(IStreamContentAccessor sa) throws CoreException { + InputStream is = sa.getContents(); + String encoding = null; + if (sa instanceof IEncodedStreamContentAccessor) + encoding = ((IEncodedStreamContentAccessor) sa).getCharset(); + if (encoding == null) + encoding = "UTF-8"; //$NON-NLS-1$ + return readString(is, encoding); + } + + /* + * Returns null if an error occurred. + */ + private static String readString(InputStream is, String encoding) { + if (is == null) + return null; + BufferedReader reader = null; + try { + StringBuffer buffer = new StringBuffer(); + char[] part = new char[2048]; + int read = 0; + reader = new BufferedReader(new InputStreamReader(is, encoding)); + + while ((read = reader.read(part)) != -1) + buffer.append(part, 0, read); + + return buffer.toString(); + + } catch (IOException ex) { + // NeedWork + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ex) { + // silently ignored + } + } + } + return null; + } + + protected Attributes sortAttributes(Attributes attrs) { + AttributesImpl attributes = new AttributesImpl(); + int len = (attrs != null) ? attrs.getLength() : 0; + for (int i = 0; i < len; i++) { + String name = attrs.getQName(i); + int count = attributes.getLength(); + int j = 0; + while (j < count) { + if (name.compareTo(attributes.getQName(j)) < 0) + break; + j++; + } + attributes.insertAttributeAt(j, name, attrs.getType(i), attrs.getValue(i)); + } + return attributes; + + } + + public void setIdMap(String idmap) { + fIdMapToUse = idmap; + } + + public String getIdMap() { + return fIdMapToUse; + } + + protected boolean isWhiteSpace(char c) { + return c == '\t' || c == '\n' || c == '\r' || c == ' '; + } + + protected String removeWhiteSpace(String str) { + str = trimWhiteSpace(str); + StringBuffer retStr = new StringBuffer(); + int start = 0, end = 0; + outer_while: while (true) { + while (end < str.length() && !isWhiteSpace(str.charAt(end))) { + end++; + } + if (end > str.length()) + break outer_while; + if (start != 0) + retStr.append(' '); + retStr.append(str.substring(start, end)); + end++; + while (end < str.length() && isWhiteSpace(str.charAt(end))) { + end++; + } + start = end; + } + return retStr.toString(); + } + + protected String trimWhiteSpace(String str) { + int start = 0, end = str.length() - 1; + while (start < str.length() && isWhiteSpace(str.charAt(start))) { + start++; + } + if (start == str.length()) + return ""; //$NON-NLS-1$ + while (end >= 0 && isWhiteSpace(str.charAt(end))) { + end--; + } + return str.substring(start, end + 1); + } + + public void setRemoveWhiteSpace(boolean removeWhiteSpace) { + fRemoveWhiteSpace = removeWhiteSpace; + } + + public boolean getRemoveWhiteSpace() { + return fRemoveWhiteSpace; + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureMapping.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureMapping.java new file mode 100644 index 0000000000..67857c7571 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureMapping.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.eclipse.compare.CompareUI; +import org.eclipse.pde.internal.ui.PDEPluginImages; + +/** + * This class is the plug-in runtime class for the + * <code>"org.eclipse.compare.xml"</code> plug-in. + * </p> + */ +public final class XMLStructureMapping { + + public static final String PLUGIN_ID= "org.eclipse.compare.examples.xml"; //$NON-NLS-1$ + public static final String ECLIPSE_PLUGIN = "Eclipse Plugin"; //$NON-NLS-1$ + public static final String ECLIPSE_SCHEMA = "Eclipse Schema"; + public static final String IMAGE_TYPE_PREFIX = "xml_"; //$NON-NLS-1$ + + private static HashMap fMappings; + private static HashMap fOrderedMappings; + static { + fMappings = new HashMap(); + HashMap idmapHM = new HashMap(); + idmapHM.put(getMapString("plugin"), "id"); //$NON-NLS-1$ //$NON-NLS-2$ + idmapHM.put(getMapString("plugin>requires>import"), "plugin"); //$NON-NLS-1$ //$NON-NLS-2$ + idmapHM.put(getMapString("plugin>runtime>library"), "name"); //$NON-NLS-1$ //$NON-NLS-2$ + idmapHM.put(getMapString("plugin>runtime>library>export"), "name"); //$NON-NLS-1$ //$NON-NLS-2$ + idmapHM.put(getMapString("plugin>extension-point"), "id"); //$NON-NLS-1$ //$NON-NLS-2$ + idmapHM.put(getMapString("plugin>extension"), "point"); //$NON-NLS-1$ //$NON-NLS-2$ + fMappings.put(ECLIPSE_PLUGIN, idmapHM); + + fOrderedMappings = new HashMap(); + ArrayList orderedList = new ArrayList(); + orderedList.add(getMapString("plugin")); + orderedList.add(getMapString("plugin>requires")); + orderedList.add(getMapString("plugin>runtime")); + orderedList.add(getMapString("plugin>extension-point")); + orderedList.add(getMapString("plugin>extension")); + fOrderedMappings.put(ECLIPSE_PLUGIN, orderedList); + + CompareUI.registerImageDescriptor(getImageKey(XMLStructureCreator.TYPE_ROOT), PDEPluginImages.DESC_PLUGIN_OBJ); + CompareUI.registerImageDescriptor(getImageKey(XMLStructureCreator.TYPE_EXTENSION), PDEPluginImages.DESC_EXTENSION_OBJ); + CompareUI.registerImageDescriptor(getImageKey(XMLStructureCreator.TYPE_EXTENSIONPOINT), PDEPluginImages.DESC_EXT_POINT_OBJ); + CompareUI.registerImageDescriptor(getImageKey(XMLStructureCreator.TYPE_ELEMENT), PDEPluginImages.DESC_XML_ELEMENT_OBJ); + CompareUI.registerImageDescriptor(getImageKey(XMLStructureCreator.TYPE_ATTRIBUTE), PDEPluginImages.DESC_ATT_URI_OBJ); + CompareUI.registerImageDescriptor(getImageKey(XMLStructureCreator.TYPE_TEXT), PDEPluginImages.DESC_XML_TEXT_NODE); + } + + protected static String getImageKey(String xmlType) { + return IMAGE_TYPE_PREFIX + xmlType; + } + + private static String getMapString(String signature) { + return XMLStructureCreator.ROOT_ID + XMLStructureCreator.SIGN_SEPARATOR + signature + XMLStructureCreator.SIGN_SEPARATOR; + } + + public static HashMap getMappings() { + return fMappings; + } + + public static HashMap getOrderedMappings() { + return fOrderedMappings; + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureViewer.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureViewer.java new file mode 100644 index 0000000000..88c50640e1 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureViewer.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.ITypedElement; +import org.eclipse.compare.structuremergeviewer.DiffNode; +import org.eclipse.compare.structuremergeviewer.ICompareInput; +import org.eclipse.compare.structuremergeviewer.IStructureComparator; +import org.eclipse.compare.structuremergeviewer.StructureDiffViewer; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerSorter; +import org.eclipse.pde.internal.ui.PDEPlugin; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.PlatformUI; + +/** + * An XML diff tree viewer that can be configured with a <code>IStructureCreator</code> + * to retrieve a hierarchical structure from the input object (an <code>ICompareInput</code>) + * and perform a two-way or three-way compare on it. + * <p> + * This class may be instantiated; it is not intended to be subclassed outside + * this package. + * </p> + * + * @see ICompareInput + */ +public class XMLStructureViewer extends StructureDiffViewer { + + class XMLSorter extends ViewerSorter { + + public XMLSorter() { + super(); + } + + public int category(Object node) { + if (node instanceof DiffNode) { + Object o = ((DiffNode) node).getId(); + if (o instanceof XMLNode) { + String xmlType= ((XMLNode) o).getXMLType(); + if (xmlType.equals(XMLStructureCreator.TYPE_ATTRIBUTE)) + return 1; + if (xmlType.equals(XMLStructureCreator.TYPE_ELEMENT) + || xmlType.equals(XMLStructureCreator.TYPE_TEXT) + || xmlType.equals(XMLStructureCreator.TYPE_EXTENSION) + || xmlType.equals(XMLStructureCreator.TYPE_EXTENSIONPOINT)) + return 2; + } + } + return 0; + } + + public void sort(final Viewer viewer, Object[] elements) { + if (elements != null + && elements.length > 0 + && elements[0] instanceof DiffNode) { + Object o = ((DiffNode) elements[0]).getId(); + if (o instanceof XMLNode) { + XMLNode parent = ((XMLNode) o).getParent(); + String sig = parent.getSignature(); + if (sig.endsWith(XMLStructureCreator.SIGN_ELEMENT)) { + final ArrayList originalTree = + new ArrayList(Arrays.asList(parent.getChildren())); + Arrays.sort(elements, new Comparator() { + public int compare(Object a, Object b) { + return XMLSorter.this.compare( + (DiffNode) a, + (DiffNode) b, + originalTree); + } + }); + return; + } + } + } + super.sort(viewer, elements); + } + + private int compare(DiffNode a, DiffNode b, ArrayList originalTree) { + + int index_a = originalTree.indexOf(a.getId()); + int index_b = originalTree.indexOf(b.getId()); + if (index_a < index_b) + return -1; + return 1; + } + } + + /** + * Creates a new viewer under the given SWT parent with the specified configuration. + * + * @param parent the SWT control under which to create the viewer + * @param configuration the configuration for this viewer + */ + public XMLStructureViewer(Composite parent, CompareConfiguration configuration) { + super(parent, configuration); + setStructureCreator(new XMLStructureCreator()); + setSorter(new XMLSorter()); + } + + + protected XMLStructureCreator getXMLStructureCreator() { + return (XMLStructureCreator) getStructureCreator(); + } + + + /* + * Recreates the comparable structures for the input sides. + */ + protected void compareInputChanged(ICompareInput input) { + if (input != null) { + ITypedElement t = input.getLeft(); + if (t != null) { + String fileExtension = t.getType(); + getXMLStructureCreator().setFileExtension(fileExtension); + } + } + + getXMLStructureCreator().initIdMaps(); + super.compareInputChanged(input); + + } + + + public IRunnableWithProgress getMatchingRunnable( + final XMLNode left, + final XMLNode right, + final XMLNode ancestor) { + + return new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) throws + InvocationTargetException, + InterruptedException, + OperationCanceledException { + + if (monitor == null) + monitor = new NullProgressMonitor(); + + int totalWork; + if (ancestor != null) + totalWork = 1; + else + totalWork = 3; + monitor.beginTask("Running Matching algorithm...", totalWork); + + AbstractMatching m = new OrderedMatching(); + try { + m.match(left, right, false, monitor); + if (ancestor != null) { + m.match(left, ancestor, true, + new SubProgressMonitor(monitor, 1)); + m.match(right, ancestor, true, + new SubProgressMonitor(monitor, 1)); + } + } finally { + monitor.done(); + } + } + }; + } + + protected void preDiffHook( + IStructureComparator ancestor, + IStructureComparator left, + IStructureComparator right) { + if (left != null && right != null) { + try { + PlatformUI.getWorkbench().getProgressService().run(true, true, + getMatchingRunnable((XMLNode) left, (XMLNode) right, (XMLNode) ancestor)); + } catch (Exception e) { + PDEPlugin.log(e); + } + } + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureViewerCreator.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureViewerCreator.java new file mode 100644 index 0000000000..91358bdccc --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/compare/XMLStructureViewerCreator.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.compare; + +import org.eclipse.swt.widgets.Composite; + +import org.eclipse.jface.viewers.Viewer; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.IViewerCreator; + +/** + * A factory object for the <code>TextMergeViewer</code>. + * This indirection is necessary because only objects with a default + * constructor can be created via an extension point + * (this precludes Viewers). + */ +public class XMLStructureViewerCreator implements IViewerCreator { + + public Viewer createViewer(Composite parent, CompareConfiguration mp) { + return new XMLStructureViewer(parent, mp); + } +} |