001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Ethan Hugg
026: *
027: * Alternatively, the contents of this file may be used under the terms of
028: * the GNU General Public License Version 2 or later (the "GPL"), in which
029: * case the provisions of the GPL are applicable instead of those above. If
030: * you wish to allow use of your version of this file only under the terms of
031: * the GPL and not to allow others to use your version of this file under the
032: * MPL, indicate your decision by deleting the provisions above and replacing
033: * them with the notice and other provisions required by the GPL. If you do
034: * not delete the provisions above, a recipient may use your version of this
035: * file under either the MPL or the GPL.
036: *
037: * ***** END LICENSE BLOCK ***** */
038:
039: package org.mozilla.javascript.xml.impl.xmlbeans;
040:
041: import org.apache.xmlbeans.XmlCursor;
042:
043: import java.util.*;
044:
045: public class LogicalEquality {
046: public static boolean nodesEqual(XmlCursor xmlOne, XmlCursor xmlTwo) {
047: boolean result = false;
048:
049: if (xmlOne.isStartdoc()) {
050: xmlOne.toFirstContentToken();
051: }
052:
053: if (xmlTwo.isStartdoc()) {
054: xmlTwo.toFirstContentToken();
055: }
056:
057: if (xmlOne.currentTokenType() == xmlTwo.currentTokenType()) {
058: if (xmlOne.isEnddoc()) {
059: // Both empty
060: result = true;
061: } else if (xmlOne.isAttr()) {
062: result = attributesEqual(xmlOne, xmlTwo);
063: } else if (xmlOne.isText()) {
064: result = textNodesEqual(xmlOne, xmlTwo);
065: } else if (xmlOne.isComment()) {
066: result = commentsEqual(xmlOne, xmlTwo);
067: } else if (xmlOne.isProcinst()) {
068: result = processingInstructionsEqual(xmlOne, xmlTwo);
069: } else if (xmlOne.isStart()) {
070: // Compare root elements
071: result = elementsEqual(xmlOne, xmlTwo);
072: }
073: }
074:
075: return result;
076: }
077:
078: private static boolean elementsEqual(XmlCursor xmlOne,
079: XmlCursor xmlTwo) {
080: boolean result = true;
081:
082: if (!qnamesEqual(xmlOne.getName(), xmlTwo.getName())) {
083: result = false;
084: } else {
085: // These filter out empty text nodes.
086: nextToken(xmlOne);
087: nextToken(xmlTwo);
088:
089: do {
090: if (xmlOne.currentTokenType() != xmlTwo
091: .currentTokenType()) {
092: // Not same token
093: result = false;
094: break;
095: } else if (xmlOne.isEnd()) {
096: // Done with this element, step over end
097: break;
098: } else if (xmlOne.isEnddoc()) {
099: // Shouldn't get here
100: break;
101: } else if (xmlOne.isAttr()) {
102: // This one will move us to the first non-attr token.
103: result = attributeListsEqual(xmlOne, xmlTwo);
104: } else {
105: if (xmlOne.isText()) {
106: result = textNodesEqual(xmlOne, xmlTwo);
107: } else if (xmlOne.isComment()) {
108: result = commentsEqual(xmlOne, xmlTwo);
109: } else if (xmlOne.isProcinst()) {
110: result = processingInstructionsEqual(xmlOne,
111: xmlTwo);
112: } else if (xmlOne.isStart()) {
113: result = elementsEqual(xmlOne, xmlTwo);
114: } else {
115: //XML.log("Unknown token type" + xmlOne.currentTokenType());
116: }
117:
118: // These filter out empty text nodes.
119: nextToken(xmlOne);
120: nextToken(xmlTwo);
121: }
122: } while (result);
123: }
124:
125: return result;
126: }
127:
128: /**
129: *
130: * @param xmlOne
131: * @param xmlTwo
132: * @return
133: */
134: private static boolean attributeListsEqual(XmlCursor xmlOne,
135: XmlCursor xmlTwo) {
136: boolean result = true;
137: TreeMap mapOne = loadAttributeMap(xmlOne);
138: TreeMap mapTwo = loadAttributeMap(xmlTwo);
139:
140: if (mapOne.size() != mapTwo.size()) {
141: result = false;
142: } else {
143: Set keysOne = mapOne.keySet();
144: Set keysTwo = mapTwo.keySet();
145: Iterator itOne = keysOne.iterator();
146: Iterator itTwo = keysTwo.iterator();
147:
148: while (result && itOne.hasNext()) {
149: String valueOne = (String) itOne.next();
150: String valueTwo = (String) itTwo.next();
151:
152: if (!valueOne.equals(valueTwo)) {
153: result = false;
154: } else {
155: javax.xml.namespace.QName qnameOne = (javax.xml.namespace.QName) mapOne
156: .get(valueOne);
157: javax.xml.namespace.QName qnameTwo = (javax.xml.namespace.QName) mapTwo
158: .get(valueTwo);
159:
160: if (!qnamesEqual(qnameOne, qnameTwo)) {
161: result = false;
162: }
163: }
164: }
165: }
166:
167: return result;
168: }
169:
170: /**
171: *
172: * @param xml
173: * @return
174: */
175: private static TreeMap loadAttributeMap(XmlCursor xml) {
176: TreeMap result = new TreeMap();
177:
178: while (xml.isAttr()) {
179: result.put(xml.getTextValue(), xml.getName());
180: nextToken(xml);
181: }
182:
183: return result;
184: }
185:
186: /**
187: *
188: * @param xmlOne
189: * @param xmlTwo
190: * @return
191: */
192: private static boolean attributesEqual(XmlCursor xmlOne,
193: XmlCursor xmlTwo) {
194: boolean result = false;
195:
196: if (xmlOne.isAttr() && xmlTwo.isAttr()) {
197: if (qnamesEqual(xmlOne.getName(), xmlTwo.getName())) {
198: if (xmlOne.getTextValue().equals(xmlTwo.getTextValue())) {
199: result = true;
200: }
201: }
202: }
203:
204: return result;
205: }
206:
207: /**
208: *
209: * @param xmlOne
210: * @param xmlTwo
211: * @return
212: */
213: private static boolean textNodesEqual(XmlCursor xmlOne,
214: XmlCursor xmlTwo) {
215: boolean result = false;
216:
217: if (xmlOne.isText() && xmlTwo.isText()) {
218: if (xmlOne.getChars().equals(xmlTwo.getChars())) {
219: result = true;
220: }
221: }
222:
223: return result;
224: }
225:
226: /**
227: *
228: * @param xmlOne
229: * @param xmlTwo
230: * @return
231: */
232: private static boolean commentsEqual(XmlCursor xmlOne,
233: XmlCursor xmlTwo) {
234: boolean result = false;
235:
236: if (xmlOne.isComment() && xmlTwo.isComment()) {
237: if (xmlOne.getTextValue().equals(xmlTwo.getTextValue())) {
238: result = true;
239: }
240: }
241:
242: return result;
243: }
244:
245: /**
246: *
247: * @param xmlOne
248: * @param xmlTwo
249: * @return
250: */
251: private static boolean processingInstructionsEqual(
252: XmlCursor xmlOne, XmlCursor xmlTwo) {
253: boolean result = false;
254:
255: if (xmlOne.isProcinst() && xmlTwo.isProcinst()) {
256: if (qnamesEqual(xmlOne.getName(), xmlTwo.getName())) {
257: if (xmlOne.getTextValue().equals(xmlTwo.getTextValue())) {
258: result = true;
259: }
260: }
261: }
262:
263: return result;
264: }
265:
266: /**
267: *
268: * @param qnameOne
269: * @param qnameTwo
270: * @return
271: */
272: private static boolean qnamesEqual(
273: javax.xml.namespace.QName qnameOne,
274: javax.xml.namespace.QName qnameTwo) {
275: boolean result = false;
276:
277: if (qnameOne.getNamespaceURI().equals(
278: qnameTwo.getNamespaceURI())) {
279: if (qnameOne.getLocalPart().equals(qnameTwo.getLocalPart())) {
280: return true;
281: }
282: }
283:
284: return result;
285: }
286:
287: /**
288: * filter out empty textNodes here
289: *
290: * @param xml
291: */
292: private static void nextToken(XmlCursor xml) {
293: do {
294: xml.toNextToken();
295:
296: if (!xml.isText()) {
297: // Not a text node
298: break;
299: } else if (xml.getChars().trim().length() > 0) {
300: // Text node is not empty
301: break;
302: }
303: } while (true);
304: }
305: }
|