/**
   * Process an MXMLTagData - this will write a String representation of the tag into the
   * StringWriter passed in. This will strip out any databinding expressions from the String, TODO:
   * databinding - add the databinding expressions as children of the MXMLXMLNode, and also record
   * what the TODO: target expressions for those are (these are the expressions to set the value in
   * the XML object when the TODO: PropertyChange event fires).
   */
  void processNode(MXMLTagData tag, StringWriter sw) {
    sw.write('<');
    if (tag.isCloseTag()) sw.write('/');
    sw.write(tag.getName());
    String tagPrefix = tag.getPrefix();

    // lookup the prefix in case it's defined elsewhere in the document outside the XML tag
    if (tagPrefix != null) lookupPrefix(tagPrefix, tag);

    List<MXMLTagAttributeData> attrs = getAttributes(tag);
    for (MXMLTagAttributeData attr : attrs) {
      sw.write(' ');
      sw.write(attr.getName());
      sw.write('=');
      sw.write('"');
      sw.write(attr.getRawValue());
      sw.write('"');

      // lookup the prefix in case it's defined outside the XML tag
      String prefix = attr.getPrefix();
      if (prefix != null) lookupPrefix(prefix, tag);
    }

    StringWriter childrenSW = new StringWriter();
    for (MXMLUnitData unit = tag.getFirstChildUnit();
        unit != null;
        unit = unit.getNextSiblingUnit()) {
      processNode(unit, childrenSW);
    }

    if (tag == rootTag) {
      // If we're the root tag, then add an xmlns for each prefix that was referenced by one of our
      // children, but was defined elsewhere in the document (like in the Application tag).
      for (String prefix : referencedPrefixes) {
        String uri = externalPrefixes.getNamespaceForPrefix(prefix);
        if (uri != null) {
          sw.write(" xmlns:");
          sw.write(prefix);
          sw.write("=\"");
          sw.write(uri);
          sw.write('\"');
        }
      }
    }
    if (tag.isEmptyTag()) {
      sw.write("/>");
    } else {
      sw.write('>');
    }
    sw.write(childrenSW.toString());

    MXMLTagData endTag = tag.findMatchingEndTag();
    if (endTag != null) {
      processNode(endTag, sw);
    }
  }
 /**
  * Helper to determine if a give MXMLUnitData is the only Text child of an MXMLTagData This
  * implies special, different processing from normal Text Datas.
  */
 private boolean isOnlyTextChild(MXMLUnitData child) {
   if (child instanceof MXMLTextData
       && ((MXMLTextData) child).getTextType() == IMXMLTextData.TextType.TEXT) {
     MXMLUnitData p = child.getParentUnitData();
     MXMLTagData parent = p instanceof MXMLTagData ? (MXMLTagData) p : null;
     if (parent != null) {
       return parent.getFirstChildUnit() == child && child.getNextSiblingUnit() == null;
     }
   }
   return false;
 }
  /**
   * Get the index of a text data. Grabs the parent, and iterates it's children to find out what the
   * index of the text data passed in should be
   */
  private int getIndexOfText(MXMLTextData text) {
    MXMLUnitData parent = text.getParentUnitData();

    MXMLTagData parentTag = parent instanceof MXMLTagData ? (MXMLTagData) parent : null;
    int index = 0;

    if (parentTag != null) {
      for (MXMLUnitData d = parentTag.getFirstChildUnit(); d != null; d = d.getNextSiblingUnit()) {
        if (d == text) break;
        else if (d instanceof MXMLTextData
            && ((MXMLTextData) d).getTextType() == IMXMLTextData.TextType.CDATA) ++index;
      }
    }
    return index;
  }
  /**
   * Generate the instructions to set the target expression based on the Data and Attribute passed
   * in. This will walk up the parent chain to the root tag, and then proceed to emit get
   * instructions for all parents from the root tag down. For the actual target, instructions to set
   * the value will be generated. If an attribute is passed in then the set instructions will target
   * the attribute instead
   *
   * @param data The UnitData to target
   * @param attr The attribute to target, or null if there is no attribute
   * @return An InstructionList that contains the instructions to set the target expression
   */
  private InstructionList getTargetInstructions(MXMLUnitData data, MXMLTagAttributeData attr) {
    MXMLUnitData d = data;
    Stack<MXMLUnitData> parentStack = new Stack<MXMLUnitData>();

    if (isOnlyTextChild(d)) {
      // If we are the only text child of a TagData, then start with the TagData,
      // as that is what we'll be setting
      d = d.getParentUnitData();
    }
    MXMLUnitData target = d;

    // push parents onto a stack, so we can walk down from the parent later
    while (d != null) {
      parentStack.add(d);
      d = d == rootTag ? null : d.getParentUnitData();
    }

    InstructionList il = new InstructionList();
    // we're always going to start with "this"
    il.addInstruction(ABCConstants.OP_getlocal0);

    // Walk down the parent stack, and emit get instructions for each tag
    // except for the last one, which is the one we're targeting
    while (parentStack.size() > 1) {
      MXMLUnitData unitData = parentStack.pop();
      if (unitData instanceof MXMLTagData) {
        generateGetInstructions(il, (MXMLTagData) unitData);
      }
    }

    if (target instanceof MXMLTagData) {
      // Targeting a Tag
      if (attr == null) {
        // Just setting the tag value
        generateSetInstructions(il, (MXMLTagData) target);
      } else {
        // We have an attr, do a get for the tag, and a set
        // for the attr
        generateGetInstructions(il, (MXMLTagData) target);
        generateSetInstructions(il, attr);
      }
    } else if (target instanceof MXMLTextData) {
      // We're targeting a TextData
      generateSetInstructions(il, (MXMLTextData) target);
    }

    return il;
  }