001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.tax;
042:
043: import java.lang.reflect.Constructor;
044: import java.lang.reflect.Field;
045: import java.lang.reflect.Modifier;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Map;
049: import junit.textui.TestRunner;
050: import org.netbeans.modules.xml.XMLDataObject;
051: import org.netbeans.modules.xml.tax.cookies.TreeEditorCookie;
052: import org.netbeans.tax.TreeNamedObjectMap.KeyListener;
053: import org.netbeans.tax.TreeDocumentType.DTDIdentity;
054: import org.netbeans.tax.event.TreeEventChangeSupport;
055: import org.netbeans.tax.event.TreeEventManager;
056: import org.netbeans.tests.xml.XTest;
057:
058: /**
059: * <P>
060: * <P>
061: * <FONT COLOR="#CC3333" FACE="Courier New, Monospaced" SIZE="+1">
062: * <B>
063: * <BR> XML Module API Test: XMLCloneTest
064: * </B>
065: * </FONT>
066: * <BR><BR><B>What it tests:</B><BR>
067: *
068: * This test clones all nodes in tested document and checks whether the clone is properly created.
069: *
070: * <BR><BR><B>How it works:</B><BR>
071: *
072: * The test pass trough the document's tree and for each node makes its clone.
073: * On clones checks his nonstatic fields.
074: *
075: * <BR><BR><B>Settings:</B><BR>
076: * none<BR>
077: *
078: * <BR><BR><B>Output (Golden file):</B><BR>
079: * For each node one line with test result.<BR>
080: *
081: * <BR><B>To Do:</B><BR>
082: * none<BR>
083: *
084: * <P>Created on April 9, 2001, 12:33 PM
085: * <P>
086: */
087: public class XMLCloneTest extends XTest {
088: /** Creates new CoreSettingsTest */
089: public XMLCloneTest(String testName) {
090: super (testName);
091: }
092:
093: // Debug variables
094: private int maxCalls = 10000;
095: private int listCount = 0;
096: private int mapCount = 0;
097: private Class testedLevel;
098:
099: // fields' patterns
100: private Pattern[] patterns = new Pattern[] {
101: // clazz, name, isCloneRoot, checker
102: new Pattern(TreeParentNode.class, null, Boolean.TRUE,
103: NullChecker.class),
104: new Pattern(TreeElement.class, "ownerElement",
105: Boolean.TRUE, NullChecker.class),
106: new Pattern(KeyListener.class, "mapKeyListener", null,
107: DifferentOrNullChecker.class),//???
108: new Pattern(TreeEventChangeSupport.class, null, null,
109: DifferentOrNullChecker.class),
110: new Pattern(TreeEventManager.class, null, null,
111: DifferentChecker.class),
112: new Pattern(String.class, null, null,
113: ImmutableChecker.class),
114: new Pattern(TreeName.class, null, null,
115: ImmutableChecker.class),
116: new Pattern(DTDIdentity.class, null, null,
117: ImmutableChecker.class), };
118:
119: public void testClone() throws Exception {
120: TreeEditorCookie cake = (TreeEditorCookie) TestUtil.THIS
121: .findData("Bookss.xml").getCookie(
122: TreeEditorCookie.class);
123: TreeDocument document = (TreeDocument) cake.openDocumentRoot();
124: nodeTest(document);
125: }
126:
127: private void nodeTest(TreeNode node) {
128: treeCloneTest(node);
129: // child test
130: if (node instanceof TreeParentNode) {
131: TreeChild child = ((TreeParentNode) node).getFirstChild();
132: while (child != null) {
133: nodeTest(child);
134: child = child.getNextSibling();
135: }
136: }
137:
138: // attribute test
139: if (node instanceof TreeElement
140: && ((TreeElement) node).hasAttributes()) {
141: Iterator attributes = ((TreeElement) node).getAttributes()
142: .iterator();
143: while (attributes.hasNext()) {
144: TreeNode attribute = (TreeNode) attributes.next();
145: treeCloneTest(attribute);
146: }
147: }
148: }
149:
150: private void treeCloneTest(TreeNode treeNode) {
151: dbg.println("\n\n========> Creating clone of: " + treeNode
152: + "::\n" + TestUtil.nodeToString(treeNode) + "\n\n");
153:
154: TreeNode treeClone = (TreeNode) treeNode.clone();
155: nodeCloneTest(treeNode, treeClone, true);
156: }
157:
158: private void nodeCloneTest(Object node, Object clone,
159: boolean isCloneRoot) {
160: dbg.println("\nNode: " + node + "\n<<<\n"
161: + TestUtil.nodeToString(node) + "\n>>>");
162: Class clazz = node.getClass();
163: do {
164: cloneLevelCheck(clazz, node, clone, isCloneRoot);
165: clazz = clazz.getSuperclass();
166: } while (clazz != null);
167:
168: // child test
169: if (node instanceof TreeParentNode) {
170: TreeChild childNode = ((TreeParentNode) node)
171: .getFirstChild();
172: TreeChild childClone = ((TreeParentNode) clone)
173: .getFirstChild();
174: while (childNode != null) {
175: if (childClone == null) {
176: err("Missing clone child: "
177: + TestUtil.nodeToString(childNode)
178: + "\nIn clone: "
179: + TestUtil.nodeToString(clone), node);
180: break;
181: }
182: nodeCloneTest(childNode, childClone, false);
183: childNode = childNode.getNextSibling();
184: childClone = childClone.getNextSibling();
185: }
186: }
187:
188: // attribute test
189: if (node instanceof TreeElement
190: && ((TreeElement) node).hasAttributes()) {
191: Iterator nodeAtrs = ((TreeElement) node).getAttributes()
192: .iterator();
193: Iterator cloneAtrs = ((TreeElement) clone).getAttributes()
194: .iterator();
195: while (nodeAtrs.hasNext()) {
196: TreeNode nodeAtr = (TreeNode) nodeAtrs.next();
197: TreeNode cloneAtr = (TreeNode) cloneAtrs.next();
198: nodeCloneTest(nodeAtr, cloneAtr, false);
199: }
200: }
201: }
202:
203: private void cloneLevelCheck(Class clazz, Object node,
204: Object clone, boolean isCloneRoot) {
205: if (maxCalls-- < 0) {//!!!
206: System.exit(1);
207: }
208:
209: dbg.println("Level: " + clazz);
210: testedLevel = clazz;
211: Field[] fields = clazz.getDeclaredFields();
212: Class checker;
213: String name;
214:
215: for (int i = 0; i < fields.length; i++) {
216: if (Modifier.isStatic(fields[i].getModifiers())) {
217: continue;
218: }
219:
220: // Checks TreeObjectList
221: if (fields[i].getType() == TreeObjectList.class) {
222: dbg.println("\n#" + listCount++
223: + ") CHECKING TREE_OBJECT_LIST: "
224: + fields[i].getName());
225: try {
226: // Get lists.
227: Field listField = TreeObjectList.class
228: .getDeclaredField("list");
229: listField.setAccessible(true);
230: fields[i].setAccessible(true);
231: List nodeList = (List) listField.get(fields[i]
232: .get(node));
233: List cloneList = (List) listField.get(fields[i]
234: .get(clone));
235:
236: // Test lists' elements
237: if (isComparable(nodeList, cloneList, node)) {
238: for (int j = 0; j < nodeList.size(); j++) {
239: nodeCloneTest((TreeNode) nodeList.get(j),
240: (TreeNode) cloneList.get(j), false);
241: }
242: }
243: } catch (Exception ex) {
244: err("In TreeObjectList Check", node);
245: ex.printStackTrace(dbg);
246: } finally {
247: dbg.println("/#" + --listCount + ") END CHECK\n");
248: }
249: }
250:
251: // Checks TreeNamedObjectMap
252: if (fields[i].getType() == TreeNamedObjectMap.class) {
253: Object key = null;
254:
255: dbg.println("\n@" + mapCount++
256: + ") CHECKING TREE_NAMED_OBJECT_MAP: "
257: + fields[i].getName());
258: try {
259: // Get maps
260: Field mapField = TreeNamedObjectMap.class
261: .getDeclaredField("map");
262: mapField.setAccessible(true);
263: fields[i].setAccessible(true);
264: Map nodeMap = (Map) mapField.get(fields[i]
265: .get(node));
266: Map cloneMap = (Map) mapField.get(fields[i]
267: .get(clone));
268:
269: // Test maps' elements
270: if (isComparable(nodeMap, cloneMap, node)) {
271:
272: Object[] keys = nodeMap.keySet().toArray();
273: for (int j = 0; j < keys.length; j++) {
274: key = keys[j];
275: nodeCloneTest(nodeMap.get(key), cloneMap
276: .get(key), false);
277: }
278: }
279: } catch (Exception ex) {
280: err("In TreeNamedObjectMap Check: key = \"" + key
281: + "\"", node);
282: ex.printStackTrace(dbg);
283: } finally {
284: dbg.println("/@" + --mapCount + ") END CHECK\n");
285: }
286: }
287:
288: // find checker and check
289: checker = DefaultChecker.class;
290: for (int j = 0; j < patterns.length; j++) {
291: if (patterns[j].compare(fields[i].getType(), fields[i]
292: .getName(), isCloneRoot)) {
293: checker = patterns[j].getChecker();
294: break;
295: }
296: }
297: newCheckerInstace(checker, fields[i], node, clone).check();
298: }
299: }
300:
301: private boolean isComparable(List nodeList, List cloneList,
302: Object node) {
303: if (cloneList == null && nodeList == null) {
304: return false;
305:
306: } else if (cloneList == null || nodeList == null) {
307: err("List is Null:" + "\nnodeList = " + nodeList
308: + "\ncloneList = " + cloneList, node);
309: return false;
310:
311: } else if (nodeList.size() != cloneList.size()) {
312: err("Lists have different size:" + "\nnodeList.size() = "
313: + nodeList.size() + "\ncloneList.size() = "
314: + cloneList.size(), node);
315: return false;
316: }
317:
318: return true;
319: }
320:
321: private boolean isComparable(Map nodeMap, Map cloneMap, Object node) {
322: if (cloneMap == null && nodeMap == null) {
323: return false;
324:
325: } else if (nodeMap != null && nodeMap.size() == 0
326: && cloneMap == null) {
327: return false;
328:
329: } else if (cloneMap == null || nodeMap == null) {
330: err("Map is Null:" + "\nnodeMap = " + nodeMap
331: + "\ncloneMap = " + cloneMap, node);
332: return false;
333:
334: } else if (nodeMap.size() != cloneMap.size()) {
335: err("Maps have different size:" + "\nnodeMap.size() = "
336: + nodeMap.size() + "\ncloneMap.size() = "
337: + cloneMap.size(), node);
338: return false;
339: }
340:
341: return true;
342: }
343:
344: private FieldChecker newCheckerInstace(Class clazz, Field field,
345: Object node, Object clone) {
346: try {
347: Constructor constructor = clazz
348: .getDeclaredConstructor(new Class[] {
349: XMLCloneTest.class, Field.class,
350: Object.class, Object.class });
351: return (FieldChecker) constructor.newInstance(new Object[] {
352: this , field, node, clone });
353: } catch (Exception e) {
354: e.printStackTrace(dbg);
355: return null;
356: }
357: }
358:
359: private String toStr(Object obj) {
360: String str = null;
361: if (obj instanceof TreeNode) {
362: try {
363: str = TestUtil.nodeToString((TreeNode) obj);
364: } catch (Exception e) {
365: }
366: ;
367: } else {
368: str = "" + obj;
369: }
370: return str;
371: }
372:
373: protected void err(String message, Object node) {
374: message = "\n!!! ERROR:"
375: + message
376: + "======================================================>"
377: + "Node: " + TestUtil.nodeToString(node) + "Level: "
378: + testedLevel;
379:
380: fail(message);
381: }
382:
383: protected String xmlTestName() {
384: return "XML-Clone-Test";
385: }
386:
387: /**
388: * Performs this testsuite.
389: * @param args the command line arguments
390: */
391: public static void main(String args[]) {
392: DEBUG = true;
393: TestRunner.run(XMLCloneTest.class);
394: }
395:
396: //@@@@
397:
398: private class Pattern {
399: private Class _clazz;
400: private String _name;
401: private Boolean _isCloneRoot;
402: private Class _checker;
403:
404: public Pattern(Class clazz, String name, Boolean isCloneRoot,
405: Class checker) {
406: _clazz = clazz;
407: _name = name;
408: _isCloneRoot = isCloneRoot;
409: _checker = checker;
410: }
411:
412: public boolean compare(Class clazz, String name,
413: boolean isCloneRoot) {
414: return ((_clazz == null) || (_clazz == clazz))
415: && ((_isCloneRoot == null) || (_isCloneRoot
416: .booleanValue() == isCloneRoot))
417: && ((_name == null) || (_name.indexOf(name) != -1));
418: }
419:
420: public Class getChecker() {
421: return _checker;
422: }
423: }
424:
425: private abstract class FieldChecker {
426: protected Field field;
427: protected Object node;
428: protected Object clone;
429:
430: public FieldChecker(Field field, Object node, Object clone) {
431: this .field = field;
432: this .node = node;
433: this .clone = clone;
434: this .field.setAccessible(true);
435: }
436:
437: public boolean check() {
438: if (test()) {
439: dbg.println(prefix() + "Field \""
440: + field.getType().getName() + "::"
441: + field.getName() + "\" is OK");
442: return true;
443: } else {
444: reportErr();
445: return false;
446: }
447: }
448:
449: protected boolean isBothNull() {
450: return (fieldFrom(node) == null)
451: && (fieldFrom(clone) == null);
452: }
453:
454: protected boolean hasDifferentID() {
455: return fieldFrom(node) != fieldFrom(clone);
456: }
457:
458: protected boolean hasSameValue() {
459: if (field.getClass().isPrimitive())
460: return fieldFrom(node).equals(fieldFrom(clone));
461: else if ((fieldFrom(node) != null)
462: && (fieldFrom(clone) == null)) {//!!!
463: return false;
464: }
465: return true;
466: }
467:
468: protected Object fieldFrom(Object obj) {
469: Object result = null;
470: try {
471: result = field.get(obj);
472: } catch (Exception e) {
473: e.printStackTrace(dbg);
474: }
475: return result;
476: }
477:
478: private void reportErr() {
479: String clazz;
480:
481: if (fieldFrom(node) != null) {
482: clazz = fieldFrom(node).getClass().getName();
483: } else {
484: clazz = "Null.clazz";
485: }
486:
487: err(prefix() + "\nClone error in field: \""
488: + field.getType().getName() + "::"
489: + field.getName() + "\"." + "\nclazz : "
490: + clazz + "\noriginal value: "
491: + toStr(fieldFrom(node)) + "\nclone value : "
492: + toStr(fieldFrom(clone)) + "\noriginal ID : "
493: + System.identityHashCode(fieldFrom(node))
494: + "\nclone ID : "
495: + System.identityHashCode(fieldFrom(clone))
496: + "\nhasDifferentID: " + hasDifferentID()
497: + "\nhasSameValue : " + hasSameValue()
498: + "\nisBoothNull : " + isBothNull(), node);
499: }
500:
501: protected abstract boolean test();
502:
503: protected abstract String prefix();
504: }
505:
506: /**
507: * Check whether the fields have (different identity and identical value)
508: * or are both Null.
509: */
510: private class DefaultChecker extends FieldChecker {
511:
512: public DefaultChecker(Field field, Object node, Object clone) {
513: super (field, node, clone);
514: }
515:
516: protected boolean test() {
517: /*
518: if (field.getName().equals("parentNode")) {
519: System.out.println("parentNode on Node:" + fieldFrom(node));
520: System.out.println("parentNode on Clone:" + fieldFrom(clone));
521: return false;
522: }
523: */
524: return (hasDifferentID() && hasSameValue()) || isBothNull();
525: }
526:
527: protected String prefix() {
528: return "DEFAULT : ";
529: }
530: }
531:
532: /**
533: * Check nothing, check() always return true.
534: */
535: private class EmptyChecker extends FieldChecker {
536:
537: public EmptyChecker(Field field, Object node, Object clone) {
538: super (field, node, clone);
539: }
540:
541: protected boolean test() {
542: return true;
543: }
544:
545: protected String prefix() {
546: return "EMPTY : ";
547: }
548: }
549:
550: /**
551: * Check whether the clone's field is Null without reference to original filed.
552: */
553: private class NullChecker extends FieldChecker {
554:
555: public NullChecker(Field field, Object node, Object clone) {
556: super (field, node, clone);
557: }
558:
559: protected boolean test() {
560: return fieldFrom(clone) == null;
561: }
562:
563: protected String prefix() {
564: return "NULL : ";
565: }
566: }
567:
568: /**
569: * Check whether the fields have same identity.
570: */
571: private class ImmutableChecker extends FieldChecker {
572:
573: public ImmutableChecker(Field field, Object node, Object clone) {
574: super (field, node, clone);
575: }
576:
577: protected boolean test() {
578: return fieldFrom(clone) == fieldFrom(node);
579: }
580:
581: protected String prefix() {
582: return "IMMUTABLE: ";
583: }
584: }
585:
586: /**
587: * Check whether the fields have different identity.
588: */
589: private class DifferentOrNullChecker extends FieldChecker {
590:
591: public DifferentOrNullChecker(Field field, Object node,
592: Object clone) {
593: super (field, node, clone);
594: }
595:
596: protected boolean test() {
597: return (fieldFrom(clone) != fieldFrom(node) || (fieldFrom(clone) == null && fieldFrom(node) == null));
598: }
599:
600: protected String prefix() {
601: return "DIFFERENT_OR_NULL: ";
602: }
603: }
604:
605: /**
606: * Check whether the fields have different identity.
607: */
608: private class DifferentChecker extends FieldChecker {
609:
610: public DifferentChecker(Field field, Object node, Object clone) {
611: super (field, node, clone);
612: }
613:
614: protected boolean test() {
615: return fieldFrom(clone) != fieldFrom(node);
616: }
617:
618: protected String prefix() {
619: return "DIFFERENT: ";
620: }
621: }
622: }
|