public void testRestrictAgainstObjectImpliesInferInheritanceHeirarchy() {

    String xml = "<?xml version=\"1.0\"?><inspection-result />";

    XmlInspectorConfig config = new XmlInspectorConfig();
    config.setRestrictAgainstObject(new JavaBeanPropertyStyle());
    config.setInferInheritanceHierarchy(true);
    config.setInputStream(new ByteArrayInputStream(xml.getBytes()));

    try {
      mInspector = new XmlInspector(config);
    } catch (InspectorException e) {
      assertEquals(
          "When using restrictAgainstObject, inferInheritanceHierarchy is implied", e.getMessage());
    }
  }
  public void testTraversalToNullTopLevelElement() {

    String xml = "<?xml version=\"1.0\"?>";
    xml +=
        "<inspection-result xmlns=\"http://www.metawidget.org/inspection-result\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.metawidget.org/inspection-result ../../inspector/inspection-result-1.0.xsd\" version=\"1.0\">";
    xml += "<entity type=\"Foo\">";
    xml += "<property name=\"bar\" type=\"Bar\"/>";
    xml += "<property name=\"baz\" type=\"Baz\"/>";
    xml += "</entity>";
    xml += "<entity type=\"Baz\">";
    xml += "<property name=\"abc\" type=\"Abc\"/>";
    xml += "</entity>";
    xml += "<entity type=\"Abc\">";
    xml += "<property name=\"def\" type=\"Def\"/>";
    xml += "</entity>";
    xml += "</inspection-result>";

    // Top level

    XmlInspectorConfig config = new XmlInspectorConfig();
    mInspector = new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));

    Document document = XmlUtils.documentFromString(mInspector.inspect(null, "Foo"));

    Element entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());
    assertEquals("Foo", entity.getAttribute(TYPE));
    assertEquals(1, entity.getAttributes().getLength());

    Element property = (Element) entity.getFirstChild();
    assertEquals(PROPERTY, property.getNodeName());
    assertEquals("bar", property.getAttribute(NAME));
    assertEquals("Bar", property.getAttribute(TYPE));
    assertEquals(2, property.getAttributes().getLength());

    property = (Element) property.getNextSibling();
    assertEquals(PROPERTY, property.getNodeName());
    assertEquals("baz", property.getAttribute(NAME));
    assertEquals("Baz", property.getAttribute(TYPE));
    assertEquals(2, property.getAttributes().getLength());

    assertEquals(2, entity.getChildNodes().getLength());

    // Missing type

    document = XmlUtils.documentFromString(mInspector.inspect(null, "Foo", "bar"));

    entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());
    assertEquals("bar", entity.getAttribute(NAME));
    assertEquals("Bar", entity.getAttribute(TYPE));
    assertEquals(2, entity.getAttributes().getLength());
    assertEquals(0, entity.getChildNodes().getLength());

    // Missing type after traverse from top

    document = XmlUtils.documentFromString(mInspector.inspect(null, "Foo", "baz"));

    entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());
    assertEquals("baz", entity.getAttribute(NAME));
    assertEquals("Baz", entity.getAttribute(TYPE));
    assertEquals(2, entity.getAttributes().getLength());
    assertEquals(1, entity.getChildNodes().getLength());

    mInspector =
        new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes()))) {

          @Override
          protected Element traverseFromTopLevelTypeToNamedChildren(Element topLevel) {

            return null;
          }
        };

    document = XmlUtils.documentFromString(mInspector.inspect(null, "Foo"));
    assertEquals(null, document);

    document = XmlUtils.documentFromString(mInspector.inspect(null, "Foo", "baz"));
    assertEquals(null, document);

    // Missing type after traverse from top (one level down)

    mInspector = new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));
    document = XmlUtils.documentFromString(mInspector.inspect(null, "Foo", "baz", "abc"));

    entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());
    assertEquals("abc", entity.getAttribute(NAME));
    assertEquals("Abc", entity.getAttribute(TYPE));
    assertEquals(2, entity.getAttributes().getLength());
    assertEquals(1, entity.getChildNodes().getLength());

    mInspector =
        new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes()))) {

          @Override
          protected Element traverseFromTopLevelTypeToNamedChildren(Element topLevel) {

            if ("Abc".equals(topLevel.getAttribute(TYPE))) {
              return null;
            }

            return topLevel;
          }
        };

    document = XmlUtils.documentFromString(mInspector.inspect(null, "Foo", "baz", "abc"));

    entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());
    assertEquals("abc", entity.getAttribute(NAME));
    assertEquals("Abc", entity.getAttribute(TYPE));
    assertEquals(2, entity.getAttributes().getLength());
    assertEquals(0, entity.getChildNodes().getLength());
  }
  public void testTraverseAgainstObject() {

    String xml = "<?xml version=\"1.0\"?>";
    xml +=
        "<inspection-result xmlns=\"http://www.metawidget.org/inspection-result\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.metawidget.org/inspection-result ../../inspector/inspection-result-1.0.xsd\" version=\"1.0\">";
    xml += "<entity type=\"" + RestrictAgainstObjectFoo.class.getName() + "\">";
    xml += "<property name=\"xmlBar\" type=\"int\"/>";
    xml += "</entity>";
    xml += "</inspection-result>";

    TraverseAgainstObjectFoo traverseAgainstObjectFoo = new TraverseAgainstObjectFoo();
    traverseAgainstObjectFoo.toTraverse = new RestrictAgainstObjectFoo();

    // Without traverseAgainstObject

    mInspector =
        new XmlInspector(
            new XmlInspectorConfig().setInputStream(new ByteArrayInputStream(xml.getBytes())));
    assertEquals(
        null,
        mInspector.inspect(
            traverseAgainstObjectFoo, TraverseAgainstObjectFoo.class.getName(), "toTraverse"));

    // With traverseAgainstObject

    XmlInspectorConfig config =
        new XmlInspectorConfig()
            .setRestrictAgainstObject(
                new JavaBeanPropertyStyle(
                    new JavaBeanPropertyStyleConfig().setSupportPublicFields(true)));
    mInspector = new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));
    Document document =
        XmlUtils.documentFromString(
            mInspector.inspect(
                traverseAgainstObjectFoo, TraverseAgainstObjectFoo.class.getName(), "toTraverse"));

    // Entity

    Element entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());

    // (should be the parent property type, so as to align the XML with other Inspectors)

    assertEquals(Object.class.getName(), entity.getAttribute(TYPE));
    assertFalse(entity.hasAttribute(NAME));

    // Properties

    Element property = (Element) entity.getFirstChild();
    assertEquals(PROPERTY, property.getNodeName());
    assertEquals("xmlBar", property.getAttribute(NAME));
    assertEquals("int", property.getAttribute(TYPE));
    assertEquals(property.getAttributes().getLength(), 2);

    assertEquals(entity.getChildNodes().getLength(), 1);

    // Traverse to a subtype

    traverseAgainstObjectFoo.toTraverse = new SubRestrictAgainstObjectFoo();

    mInspector = new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));
    document =
        XmlUtils.documentFromString(
            mInspector.inspect(
                traverseAgainstObjectFoo, TraverseAgainstObjectFoo.class.getName(), "toTraverse"));

    // Entity

    entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());

    // (should be the parent property type, so as to align the XML with other Inspectors)

    assertEquals(Object.class.getName(), entity.getAttribute(TYPE));
    assertFalse(entity.hasAttribute(NAME));

    // Properties

    property = (Element) entity.getFirstChild();
    assertEquals(PROPERTY, property.getNodeName());
    assertEquals("xmlBar", property.getAttribute(NAME));
    assertEquals("int", property.getAttribute(TYPE));
    assertEquals(property.getAttributes().getLength(), 2);

    assertEquals(entity.getChildNodes().getLength(), 1);
  }
  public void testTraverseAgainstObjectImpliesType() {

    String xml = "<?xml version=\"1.0\"?>";
    xml +=
        "<inspection-result xmlns=\"http://www.metawidget.org/inspection-result\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.metawidget.org/inspection-result ../../inspector/inspection-result-1.0.xsd\" version=\"1.0\">";
    xml += "<entity type=\"" + TraverseAgainstObjectFoo.class.getName() + "\">";
    xml += "<property name=\"toTraverse\"/>";
    xml += "</entity>";
    xml += "</inspection-result>";

    TraverseAgainstObjectFoo traverseAgainstObjectFoo = new TraverseAgainstObjectFoo();
    traverseAgainstObjectFoo.toTraverse = new TraverseAgainstObjectFoo();

    // Top level

    XmlInspectorConfig config =
        new XmlInspectorConfig()
            .setRestrictAgainstObject(
                new JavaBeanPropertyStyle(
                    new JavaBeanPropertyStyleConfig().setSupportPublicFields(true)));
    mInspector = new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));

    Document document =
        XmlUtils.documentFromString(
            mInspector.inspect(traverseAgainstObjectFoo, TraverseAgainstObjectFoo.class.getName()));

    // Entity

    Element entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());
    assertEquals(TraverseAgainstObjectFoo.class.getName(), entity.getAttribute(TYPE));
    assertEquals(1, entity.getAttributes().getLength());

    // Property

    Element property = (Element) entity.getFirstChild();
    assertEquals(PROPERTY, property.getNodeName());
    assertEquals("toTraverse", property.getAttribute(NAME));

    // (shouldn't need @type on properties, as XmlUtils.combineElements uses the @name)

    assertTrue(!property.hasAttribute(TYPE));
    assertEquals(1, property.getAttributes().getLength());

    assertEquals(1, entity.getChildNodes().getLength());

    // Sub-level

    document =
        XmlUtils.documentFromString(
            mInspector.inspect(
                traverseAgainstObjectFoo, TraverseAgainstObjectFoo.class.getName(), "toTraverse"));

    // Entity

    entity = (Element) document.getDocumentElement().getFirstChild();
    assertEquals(ENTITY, entity.getNodeName());
    assertEquals(Object.class.getName(), entity.getAttribute(TYPE));
    assertEquals("toTraverse", entity.getAttribute(NAME));
    assertEquals(2, entity.getAttributes().getLength());

    // Property

    property = (Element) entity.getFirstChild();
    assertEquals(PROPERTY, property.getNodeName());
    assertEquals("toTraverse", property.getAttribute(NAME));
    assertTrue(!property.hasAttribute(TYPE));
    assertEquals(1, property.getAttributes().getLength());

    assertEquals(1, entity.getChildNodes().getLength());
  }
  public void testValidateAgainstClasses() {

    // First entity is good, second has a bad name

    String xml = "<?xml version=\"1.0\"?>";
    xml +=
        "<inspection-result xmlns=\"http://www.metawidget.org/inspection-result\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.metawidget.org/inspection-result ../../inspector/inspection-result-1.0.xsd\" version=\"1.0\">";
    xml += "<entity type=\"" + NullObject.class.getName() + "\">";
    xml += "<property name=\"nestedNullObject\"/>";
    xml += "</entity>";
    xml += "<entity type=\"" + RestrictAgainstObjectFoo.class.getName() + "\">";
    xml += "<property name=\"baz\"/>";
    xml += "</entity>";
    xml += "</inspection-result>";

    XmlInspectorConfig config =
        new XmlInspectorConfig()
            .setRestrictAgainstObject(new JavaBeanPropertyStyle())
            .setValidateAgainstClasses(new JavaBeanPropertyStyle());

    try {
      mInspector =
          new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));
      fail();
    } catch (InspectorException e) {
      assertEquals(
          "class org.metawidget.inspector.xml.XmlInspectorTest$RestrictAgainstObjectFoo does not define a property 'baz'",
          e.getMessage());
    }

    // Bad type

    xml = "<?xml version=\"1.0\"?>";
    xml +=
        "<inspection-result xmlns=\"http://www.metawidget.org/inspection-result\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.metawidget.org/inspection-result ../../inspector/inspection-result-1.0.xsd\" version=\"1.0\">";
    xml += "<entity type=\"" + NullObject.class.getName() + "\">";
    xml += "<property name=\"nestedNullObject\" type=\"int\"/>";
    xml += "</entity>";
    xml += "</inspection-result>";

    try {
      mInspector =
          new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));
      fail();
    } catch (InspectorException e) {
      assertEquals(
          "class org.metawidget.inspector.xml.XmlInspectorTest$NullObject defines property 'nestedNullObject' to be org.metawidget.inspector.xml.XmlInspectorTest$NullObject, not 'int'",
          e.getMessage());
    }

    // Good extends

    xml = "<?xml version=\"1.0\"?>";
    xml +=
        "<inspection-result xmlns=\"http://www.metawidget.org/inspection-result\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.metawidget.org/inspection-result ../../inspector/inspection-result-1.0.xsd\" version=\"1.0\">";
    xml +=
        "<entity type=\""
            + SubRestrictAgainstObjectFoo.class.getName()
            + "\" extends=\""
            + RestrictAgainstObjectFoo.class.getName()
            + "\"/>";
    xml += "</inspection-result>";

    mInspector = new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));

    // Bad extends

    xml = "<?xml version=\"1.0\"?>";
    xml +=
        "<inspection-result xmlns=\"http://www.metawidget.org/inspection-result\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.metawidget.org/inspection-result ../../inspector/inspection-result-1.0.xsd\" version=\"1.0\">";
    xml += "<entity type=\"" + SubRestrictAgainstObjectFoo.class.getName() + "\" extends=\"Bar\"/>";
    xml += "</inspection-result>";

    try {
      mInspector =
          new XmlInspector(config.setInputStream(new ByteArrayInputStream(xml.getBytes())));
      fail();
    } catch (InspectorException e) {
      assertEquals(
          "class org.metawidget.inspector.xml.XmlInspectorTest$SubRestrictAgainstObjectFoo extends class org.metawidget.inspector.xml.XmlInspectorTest$RestrictAgainstObjectFoo, not 'Bar'",
          e.getMessage());
    }
  }