コード例 #1
0
  @Override
  public Element processInspectionResultAsDom(
      Element inspectionResult, M metawidget, Object toInspect, String type, String... names) {

    try {
      // Start a new document
      //
      // (Android 1.1 did not cope well with shuffling the nodes of an existing document)

      Document newDocument = XmlUtils.newDocument();
      Element newInspectionResult = newDocument.createElementNS(NAMESPACE, ROOT);
      XmlUtils.setMapAsAttributes(
          newInspectionResult, XmlUtils.getAttributesAsMap(inspectionResult));
      newDocument.appendChild(newInspectionResult);

      Element entity = XmlUtils.getFirstChildElement(inspectionResult);
      Element newEntity = newDocument.createElementNS(NAMESPACE, ENTITY);
      XmlUtils.setMapAsAttributes(newEntity, XmlUtils.getAttributesAsMap(entity));
      newInspectionResult.appendChild(newEntity);

      // Record all traits (ie. properties/actions) that have comes-after

      Map<Element, String[]> traitsWithComesAfter = new LinkedHashMap<Element, String[]>();
      Element trait = XmlUtils.getFirstChildElement(entity);

      while (trait != null) {

        // (if no comes-after, move them across to the new document)

        if (hasComesAfter(trait, metawidget)) {
          traitsWithComesAfter.put(trait, ArrayUtils.fromString(getComesAfter(trait, metawidget)));
        } else {
          newEntity.appendChild(XmlUtils.importElement(newDocument, trait));
        }

        trait = XmlUtils.getNextSiblingElement(trait);
      }

      // Next, sort the traits
      //
      // Note: this has high big-O complexity. We could plug-in something smarter for large
      // numbers of traits.

      int infiniteLoop = traitsWithComesAfter.size();
      infiniteLoop *= infiniteLoop;

      while (!traitsWithComesAfter.isEmpty()) {
        // Infinite loop? Explain why

        infiniteLoop--;

        if (infiniteLoop < 0) {
          List<String> comesAfterNames = CollectionUtils.newArrayList();

          for (Map.Entry<Element, String[]> entry : traitsWithComesAfter.entrySet()) {

            String value;

            if (entry.getValue().length == 0) {
              value = "at the end";
            } else {
              value = "after " + ArrayUtils.toString(entry.getValue(), " and ");
            }

            comesAfterNames.add(entry.getKey().getAttribute(NAME) + " comes " + value);
          }

          // (sort for unit tests)

          Collections.sort(comesAfterNames);

          throw InspectionResultProcessorException.newException(
              "Infinite loop detected when sorting "
                  + COMES_AFTER
                  + ": "
                  + CollectionUtils.toString(comesAfterNames, ", but "));
        }

        // For each entry in the Map...

        outer:
        for (Iterator<Map.Entry<Element, String[]>> i = traitsWithComesAfter.entrySet().iterator();
            i.hasNext(); ) {
          Map.Entry<Element, String[]> entry = i.next();
          Element traitWithComesAfter = entry.getKey();
          String[] comesAfter = entry.getValue();

          // ...if it 'comesAfter everything', make sure there are only
          // other 'comesAfter everything's left...

          if (comesAfter.length == 0) {
            for (String[] traitWithComesAfterExisting : traitsWithComesAfter.values()) {
              if (traitWithComesAfterExisting.length > 0) {
                continue outer;
              }
            }

            newEntity.appendChild(XmlUtils.importElement(newDocument, traitWithComesAfter));
          }

          // ...or, if it 'comesAfter' something, make sure none of those
          // somethings are left...

          else {
            String name = traitWithComesAfter.getAttribute(NAME);

            for (String comeAfter : comesAfter) {
              if (name.equals(comeAfter)) {
                throw InspectionResultProcessorException.newException(
                    "'" + comeAfter + "' " + COMES_AFTER + " itself");
              }

              for (Element traitExisting : traitsWithComesAfter.keySet()) {
                if (comeAfter.equals(traitExisting.getAttribute(NAME))) {
                  continue outer;
                }
              }
            }

            // Insert it at the earliest point. This seems most 'natural'

            Element newTrait = XmlUtils.getFirstChildElement(newEntity);
            Element insertBefore = newTrait;

            while (newTrait != null) {

              if (ArrayUtils.contains(comesAfter, newTrait.getAttribute(NAME))) {
                newTrait = XmlUtils.getNextSiblingElement(newTrait);
                insertBefore = newTrait;
                continue;
              }

              newTrait = XmlUtils.getNextSiblingElement(newTrait);
            }

            if (insertBefore == null) {
              newEntity.appendChild(XmlUtils.importElement(newDocument, traitWithComesAfter));
            } else {
              newEntity.insertBefore(
                  XmlUtils.importElement(newDocument, traitWithComesAfter), insertBefore);
            }
          }

          i.remove();
        }
      }

      return newInspectionResult;
    } catch (Exception e) {
      throw InspectionResultProcessorException.newException(e);
    }
  }