@Test
  public void testRemoveAttributesByMapValuesViewIterator() {

    Element p = XPATH.selectSingleNode(this.html, "body/p");
    Attribute dataB = XPATH.selectSingleNode(p, "@data-b");
    Attribute dataD = XPATH.selectSingleNode(p, "@data-d");
    Attribute dataF = XPATH.selectSingleNode(p, "@data-f");
    Assert.assertTrue(p.getAttributes().containsValue(dataB));
    Assert.assertSame(p, dataB.getOwnerElement());
    Assert.assertTrue(p.getAttributes().containsValue(dataD));
    Assert.assertSame(p, dataD.getOwnerElement());
    Assert.assertTrue(p.getAttributes().containsValue(dataF));
    Assert.assertSame(p, dataF.getOwnerElement());

    this.html.accept(
        new Visitor() {

          @Override
          public void visitElement(Element element) {
            /*
             * If we remove the attribute whose localName start with
             * "ng-" in the method "visitAttribute", we will get
             * "java.util.ConcurrentModificationException"!
             *
             * But, this method is "visitElement", we can do it safely here :)
             */
            Iterator<Attribute> valueItr = element.getAttributes().values().iterator();
            while (valueItr.hasNext()) {
              Attribute attribute = valueItr.next();
              if (attribute.getQuanifiedName().getLocalName().startsWith("data-")) {
                int number;
                try {
                  number = Integer.parseInt(attribute.getValue());
                } catch (NumberFormatException ex) {
                  continue;
                }
                if (number % 2 == 0) {
                  /*
                   * If the value of the current attribute is even number, remove it.
                   * so three attributes will be removed: data-b='4' data-d='16' data-f='36'
                   */
                  valueItr.remove();
                }
              }
            }
          }
        });

    /*
     * The bidirectional associations between the "data-b", "data-d", "data-f"
     * and their owner elements are destroyed automatically and implicitly
     */
    Assert.assertFalse(p.getAttributes().containsValue(dataB)); // Changed by you
    Assert.assertNull(dataB.getOwnerElement()); // Set to be null automatically and implicitly
    Assert.assertFalse(p.getAttributes().containsValue(dataD)); // Changed by you
    Assert.assertNull(dataD.getOwnerElement()); // Set to be null automatically and implicitly
    Assert.assertFalse(p.getAttributes().containsValue(dataF)); // Changed by you
    Assert.assertNull(dataF.getOwnerElement()); // Set to be null automatically and implicitly

    /*
     * Now the whole html is(data-b, data-d, data-f are removed)
     */
    Assert.assertEquals(
        "<html>"
            + "<head>"
            + "<link rel='stylesheet' type='text/css' href='default_theme.css'/>"
            + "<script type='text/javascript' src='angular.min.js'></script>"
            + "</head>"
            + "<body ng-app='' ng-controller='simpleDemoCtrl'>"
            + "<p data-a='1' data-c='9' data-e='25' data-g='49'>"
            + "<input type='text' class='wide-input' ng-model='keyword'/>"
            + "</p>"
            + "<div class='simple-list'>"
            + "<div ng-repeat='item in items()'>"
            + "<span ng-bind='item.name'></span>"
            + "</div>"
            + "</div>"
            + "</body>"
            + "</html>",
        XML_BUILDER.build(this.html));
  }
 @Before
 public void create() {
   this.html =
       new Element(
           "html",
           new Element(
               "head",
               new Element(
                   "link",
                   new Attribute("rel", "stylesheet"),
                   new Attribute("type", "text/css"),
                   new Attribute("href", "default_theme.css")),
               new Element(
                   "script",
                   new Attribute("type", "text/javascript"),
                   new Attribute("src", "angular.min.js"),
                   new Text(""))),
           new Element(
               "body",
               new Attribute("ng-app", ""),
               new Attribute("ng-controller", "simpleDemoCtrl"),
               new Element(
                   "p",
                   new Attribute("data-a", "1"),
                   new Attribute("data-b", "4"),
                   new Attribute("data-c", "9"),
                   new Attribute("data-d", "16"),
                   new Attribute("data-e", "25"),
                   new Attribute("data-f", "36"),
                   new Attribute("data-g", "49"),
                   new Element(
                       "input",
                       new Attribute("type", "text"),
                       new Attribute("class", "wide-input"),
                       new Attribute("ng-model", "keyword"))),
               new Element(
                   "div",
                   new Attribute("class", "simple-list"),
                   new Element(
                       "div",
                       new Attribute("ng-repeat", "item in items()"),
                       new Element(
                           "span", new Attribute("ng-bind", "item.name"), new Text(""))))));
   Assert.assertEquals(
       "<html>"
           + "<head>"
           + "<link rel='stylesheet' type='text/css' href='default_theme.css'/>"
           + "<script type='text/javascript' src='angular.min.js'></script>"
           + "</head>"
           + "<body ng-app='' ng-controller='simpleDemoCtrl'>"
           + "<p data-a='1' data-b='4' data-c='9' data-d='16' data-e='25' data-f='36' data-g='49'>"
           + "<input type='text' class='wide-input' ng-model='keyword'/>"
           + "</p>"
           + "<div class='simple-list'>"
           + "<div ng-repeat='item in items()'>"
           + "<span ng-bind='item.name'></span>"
           + "</div>"
           + "</div>"
           + "</body>"
           + "</html>",
       XML_BUILDER.build(this.html));
 }
  @Test
  public void testRemoveAttributesByMapKeySetViewIterator() {

    Element body = XPATH.selectSingleNode(this.html, "body");
    Attribute ngApp = XPATH.selectSingleNode(body, "@ng-app");
    Attribute ngController = XPATH.selectSingleNode(body, "@ng-controller");
    Assert.assertTrue(body.getAttributes().containsValue(ngApp));
    Assert.assertSame(body, ngApp.getOwnerElement());
    Assert.assertTrue(body.getAttributes().containsValue(ngController));
    Assert.assertSame(body, ngController.getOwnerElement());

    Element input = XPATH.selectSingleNode(body, "p/input");
    Attribute ngModel = XPATH.selectSingleNode(input, "@ng-model");
    Assert.assertTrue(input.getAttributes().containsValue(ngModel));
    Assert.assertSame(input, ngModel.getOwnerElement());

    Element div = XPATH.selectSingleNode(body, "div/div");
    Attribute ngRepeat = XPATH.selectSingleNode(div, "@ng-repeat");
    Assert.assertTrue(div.getAttributes().containsValue(ngRepeat));
    Assert.assertSame(div, ngRepeat.getOwnerElement());

    Element span = XPATH.selectSingleNode(div, "span");
    Attribute ngBind = XPATH.selectSingleNode(span, "@ng-bind");
    Assert.assertTrue(span.getAttributes().containsValue(ngBind));
    Assert.assertSame(span, ngBind.getOwnerElement());

    this.html.accept(
        new Visitor() {

          @Override
          public void visitElement(Element element) {
            /*
             * If we remove the attribute whose localName start with
             * "ng-" in the method "visitAttribute", we will get
             * "java.util.ConcurrentModificationException"!
             *
             * But, this method is "visitElement", we can do it safely here:)
             */
            Iterator<QuanifiedName> keyItr = element.getAttributes().keySet().iterator();
            while (keyItr.hasNext()) {
              QuanifiedName attributeName = keyItr.next();
              if (attributeName.getLocalName().startsWith("ng-")) {
                keyItr.remove();
              }
            }
          }
        });

    /*
     * The bidirectional associations between the "ng-" attributes and
     * their owner elements are destroyed automatically and implicitly
     */
    Assert.assertFalse(body.getAttributes().containsValue(ngApp)); // Changed by you
    Assert.assertNull(ngApp.getOwnerElement()); // Set to be null automatically and implicitly
    Assert.assertFalse(body.getAttributes().containsValue(ngController));
    Assert.assertNull(ngController.getOwnerElement());

    Assert.assertFalse(input.getAttributes().containsValue(ngModel)); // Changed by you
    Assert.assertNull(ngModel.getOwnerElement()); // Set to be null automatically and implicitly

    Assert.assertFalse(div.getAttributes().containsValue(ngRepeat)); // Changed by you
    Assert.assertNull(ngRepeat.getOwnerElement()); // Set to be null automatically and implicitly

    Assert.assertFalse(span.getAttributes().containsValue(ngBind)); // Changed by you
    Assert.assertNull(ngBind.getOwnerElement()); // Set to be null automatically and implicitly

    /*
     * Now the whole html is(all the attributes start with "ng-" are removed)
     */
    Assert.assertEquals(
        "<html>"
            + "<head>"
            + "<link rel='stylesheet' type='text/css' href='default_theme.css'/>"
            + "<script type='text/javascript' src='angular.min.js'></script>"
            + "</head>"
            + "<body>"
            + "<p data-a='1' data-b='4' data-c='9' data-d='16' data-e='25' data-f='36' data-g='49'>"
            + "<input type='text' class='wide-input'/>"
            + "</p>"
            + "<div class='simple-list'>"
            + "<div>"
            + "<span></span>"
            + "</div>"
            + "</div>"
            + "</body>"
            + "</html>",
        XML_BUILDER.build(this.html));
  }