public Object clone() {
   XMLTag ThisClone = new XMLTag(TagName, TagValue, IsTemporary);
   ThisClone.AttributeList = (HashMap) AttributeList.clone();
   for (int i = 0; i < ChildTags.size(); i++) {
     XMLTag TagToClone = (XMLTag) ChildTags.get(i);
     ThisClone.addTag((XMLTag) TagToClone.clone());
   }
   return ThisClone;
 }
 /**
  * Sets the value for the tag/attribute found at TagPath.
  *
  * <p>If TagPath ends with "@<I>value</I>" then it will attempt to return the values of the
  * attribute <I>value</I> from the tags described in the first portion of TagPath.
  *
  * @param TagPath The path to the value to be returned.
  * @param _TagValue The new value.
  * @return The old value.
  */
 public boolean setValue(String TagPath, String _TagValue) {
   // Returns true if the tagpath existed, false if it was created.
   if (TagPath.startsWith("@")) {
     String TempString = getAttribute(TagPath.substring(1));
     setAttribute(TagPath.substring(1), _TagValue);
     if (TempString == null) {
       return false;
     } else {
       return true;
     }
   }
   String[] TempString = TagPath.split("/", 2);
   XMLTag TempTag = getTag(TempString[0]);
   if (TempString.length == 1) {
     if (TempTag == null) {
       addTag(TempString[0], _TagValue);
       return false;
     } else {
       return TempTag.setValue(_TagValue);
     }
   } else {
     if (TempTag == null) {
       TempTag = addTag(TempString[0]);
     }
     return TempTag.setValue(TempString[1], _TagValue);
   }
 }
 /**
  * Removes this tag from its parent.
  *
  * @return The old parent.
  */
 public XMLTag removeTagFromParent() {
   if (ParentTag == null) {
     return null;
   } else {
     XMLTag OldParent = ParentTag;
     OldParent.removeChild(this);
     return OldParent;
   }
 }
 /**
  * Adds NewChild as a child, but doesn't alter NewChild's parent status.
  *
  * <p>This means that the child tag will turn up in a search of this tags children, but this tag
  * will not be locatable from the child.
  *
  * @param NewChild The tag to share.
  * @return The real parent of NewChild.
  */
 public XMLTag shareTag(XMLTag NewChild) {
   if (NewChild != null) {
     ChildTags.add(NewChild);
     NewChild.addXMLTagListener(this);
     ChildrenCount++;
     fireTagAdded(new XMLPath());
   }
   return NewChild.getParentTag();
 }
 /**
  * Returns all child tags that correspond to the paths in TagPath.
  *
  * @param TagPath The paths from which to retrieve the tags.
  * @return All child tags that correspond to one of the paths in TagPath.
  */
 public XMLTag[] getTags(String[] TagPaths) {
   String TagPath;
   XMLTag[][] Results = new XMLTag[TagPaths.length][];
   for (int q = 0; q < TagPaths.length; q++) {
     TagPath = TagPaths[q];
     if (TagPath.startsWith("/")) {
       return getRootTag().getTags(TagPath.substring(1));
     }
     if (TagPath.trim().equals("")) {
       return getTags();
     }
     String[] TempStrings = TagPath.split("/", 2);
     if (TempStrings.length == 1) {
       if (TagPath.equals(".") || (this instanceof XMLFile && TagPath.equals(TagName))) {
         Results[q] = new XMLTag[] {this};
       } else if (TagPath.equals("..")) {
         if (ParentTag != null) {
           Results[q] = new XMLTag[] {ParentTag};
         } else {
           Results[q] = new XMLTag[0];
         }
       } else if (TagPath.equals("*")) {
         return (XMLTag[]) ChildTags.toArray(new XMLTag[0]);
       }
       ArrayList TemporaryStore = new ArrayList();
       // XMLTag[] Result = new XMLTag[0];
       for (int i = 0; i < ChildTags.size(); i++) {
         XMLTag TempTag = (XMLTag) ChildTags.get(i);
         if (TempTag.getName().equals(TagPath)) {
           TemporaryStore.add(TempTag);
         }
       }
       Results[q] = (XMLTag[]) TemporaryStore.toArray(new XMLTag[0]);
     } else {
       XMLTag[] Result = getTags(TempStrings[0]);
       ArrayList TemporaryStore = new ArrayList();
       for (int i = 0; i < Result.length; i++) {
         TemporaryStore.addAll(Arrays.asList(Result[i].getTags(TempStrings[1])));
       }
       Results[q] = (XMLTag[]) TemporaryStore.toArray(new XMLTag[0]);
     }
   }
   int Counter = 0;
   for (int i = 0; i < Results.length; i++) {
     Counter += Results[i].length;
   }
   XMLTag[] Result = new XMLTag[Counter];
   Counter = 0;
   for (int i = 0; i < Results.length; i++) {
     for (int j = 0; j < Results[i].length; j++) {
       Result[Counter] = Results[i][j];
       Counter++;
     }
   }
   return Result;
 }
 /**
  * Replaces <CODE>ParentTag.getTag(OldTagName, OccurrenceNumber)</CODE> with this tag. If not
  * enough tags are found, then this tag is just added to ParentTag.
  *
  * @param ParentTag The parent tag to search from.
  * @param OldTagName The path to the tag to be replaced.
  * @param OccurrenceNumber The occurrence number of the path in OldTagName to be replaced.
  * @return The tag that has been replaced if one existed, else null.
  */
 public XMLTag replace(XMLTag _ParentTag, String OldTagName, int OccurrenceNumber) {
   XMLTag OldTag = _ParentTag.getTag(OldTagName, OccurrenceNumber);
   if (OldTag != null) {
     // OldTag.removeTagFromParent().addTag(this);
     _ParentTag.exchangeChild(OldTag, this);
   } else {
     _ParentTag.addTag(this);
   }
   return OldTag;
 }
 /**
  * Removes UnwantedChild from the child tags.
  *
  * @param UnwantedChild The child to be removed.
  */
 public void removeChild(XMLTag UnwantedChild) {
   if (ChildTags.contains(UnwantedChild)) {
     ChildTags.remove(ChildTags.indexOf(UnwantedChild));
     if (UnwantedChild.getParentTag() == this) {
       UnwantedChild.setParent(null);
     } else {
       UnwantedChild.removeXMLTagListener(this);
     }
     ChildrenCount--;
     fireTagRemoved(new XMLPath());
   }
 }
 /**
  * Replaces OldTag with this tag.
  *
  * @param OldTag The tag to be replaced.
  * @return The parent tag.
  */
 public XMLTag replace(XMLTag OldTag) {
   if (OldTag == null) {
     return null;
   }
   XMLTag NewParentTag = OldTag.getParentTag(); // removeTagFromParent();
   if (NewParentTag != null) {
     // NewParentTag.addTag(this);
     NewParentTag.exchangeChild(OldTag, this);
   }
   ParentTag = NewParentTag;
   OldTag.setParent(null);
   return NewParentTag;
 }
 /**
  * The value of the OccurrenceNumber-th tag defined by TagPath.
  *
  * <p>OccurrenceNumber begins indexing from 1.
  *
  * <p>If TagPath ends with "@<I>value</I>" then it will attempt to return the value of the
  * attribute <I>value</I> from the tag described in the first portion of TagPath.
  *
  * @param TagPath The path to the value to be returned.
  * @param OccurrenceNumber The occurrence of the desired tag to be looked at.
  * @return The value of the OccurrenceNumber-th TagPath, if that many exist, else null.
  */
 public String getValue(String TagPath, int OccurrenceNumber) {
   if (TagPath.startsWith("/")) {
     return getRootTag().getValue(TagPath.substring(1), OccurrenceNumber);
   }
   String[] TempStrings = TagPath.split("/?@");
   XMLTag Result = getTag(TempStrings[0], OccurrenceNumber);
   if (Result != null && TempStrings.length > 1) {
     return Result.getAttribute(TempStrings[1]);
   } else if (Result != null) {
     return Result.getValue();
   } else {
     return null;
   }
 }
 public boolean write(OutputStream WritingStream) {
   if (ParentTag == null) {
     return false;
   } else {
     return ParentTag.write(WritingStream);
   }
 }
 /**
  * Calls write() on the parent tag if one exists. This has the effect of writing an XMLFile if one
  * exists in the ancestory.
  *
  * @return <CODE>true</CODE> if an XMLFile was written else <CODE>false</CODE>.
  */
 public boolean write() {
   if (ParentTag == null) {
     return false;
   } else {
     return ParentTag.write();
   }
 }
 /**
  * Returns the root tag of the XML structure.
  *
  * @return The first ancestor to have no parent.
  */
 public XMLTag getRootTag() {
   if (ParentTag == null) {
     return this;
   } else {
     return ParentTag.getRootTag();
   }
 }
 /**
  * Returns the first tag corresponding to TagPath.
  *
  * @param TagPath The path to the desired tag.
  * @return The first tag corresponding to TagPath if one exists, else null.
  */
 public XMLTag getTag(String TagPath) {
   if (TagPath.startsWith("/")) {
     return getRootTag().getTag(TagPath.substring(1));
   }
   if (TagPath.trim().equals("")) {
     return this;
   }
   String[] TempStrings = TagPath.split("/", 2);
   if (TempStrings.length == 1) {
     if (TagPath.equals(".") || (this instanceof XMLFile && TagPath.equals(TagName))) {
       return this;
     }
     if (TagPath.equals("..")) {
       return ParentTag;
     }
     XMLTag[] Result = new XMLTag[0];
     Result = (XMLTag[]) ChildTags.toArray(new XMLTag[0]);
     int CurrentIndex = 0;
     while (CurrentIndex < Result.length && !Result[CurrentIndex].getName().equals(TagPath)) {
       CurrentIndex++;
     }
     if (CurrentIndex < Result.length) {
       return Result[CurrentIndex];
     } else {
       return null;
     }
   } else {
     XMLTag Result;
     if (TempStrings[0].equals("*")) {
       XMLTag[] TempTags = (XMLTag[]) ChildTags.toArray(new XMLTag[0]);
       for (int i = 0; i < TempTags.length; i++) {
         Result = TempTags[i].getTag(TempStrings[1]);
         if (Result != null) {
           return Result;
         }
       }
       return null;
     } else {
       Result = getTag(TempStrings[0]);
       if (Result != null) {
         return Result.getTag(TempStrings[1]);
       } else {
         return null;
       }
     }
   }
 }
 /**
  * Sets the parent of this tag to NewParent.
  *
  * @param NewParent The new parent tag.
  * @return The old parent tag.
  */
 protected XMLTag setParent(XMLTag NewParent) {
   if (ParentTag != null) {
     XMLTag OldParent = ParentTag;
     OldParent.removeChild(this);
     ParentTag = NewParent; /*
                              * if(NewParent != null) {
                              * addXMLTagListener(NewParent); }
                              */
     return OldParent;
   } else {
     ParentTag = NewParent; /*
                              * if(NewParent != null) {
                              * addXMLTagListener(NewParent); }
                              */
     return null;
   }
 }
 /**
  * Constructor to create a tag with a value as the child of another tag, which can be set as
  * temporary.
  *
  * <p>If a tag is deemed temporary then it will occur in all searches etc. but when the tag is
  * output (with writeTag() or as part of an XMLFile) the tag will not be included.
  *
  * @param TagName The name of the new tag.
  * @param TagValue The value of the new tag.
  * @param ParentTag The tag which will be the parent of the new tag.
  * @param IsTemporary <CODE>true</CODE> if the tag is temporary, <CODE>false</CODE> if not.
  */
 public XMLTag(String _TagName, String _TagValue, XMLTag _ParentTag, boolean _IsTemporary) {
   this.ParentTag = _ParentTag;
   this.TagName = _TagName;
   this.TagValue = _TagValue;
   this.IsTemporary = _IsTemporary;
   if (ParentTag != null) {
     ParentTag.addTag(this);
   }
 }
 protected void fireTagChanged(XMLPath PathToTag) {
   XMLTagListener[] listeners = (XMLTagListener[]) Listeners.getListeners(XMLTagListener.class);
   for (int i = 0; i < listeners.length; i++) {
     listeners[i].tagChanged(new XMLTagEvent(this, PathToTag));
   }
   if (ParentTag != null) {
     ParentTag.fireTagChanged(new XMLPath(this, PathToTag, true));
   }
 }
 /**
  * Adds NewChild as a child tag, and sets it's parent to be this tag.
  *
  * @param NewChild The new tag to add.
  * @return The old parent of NewChild.
  */
 public XMLTag addTag(XMLTag NewChild) {
   if (NewChild != null) {
     ChildTags.add(NewChild);
     NewChild.setParent(this);
     ChildrenCount++;
     fireTagAdded(new XMLPath());
   }
   return NewChild;
 }
 /**
  * Returns all child tags that actually exist that correspond to TagPath. Written in anger by Wes
  * in response to the ridiculous Temporary Tags.
  *
  * @param TagPath The path from which to retrieve the tags.
  * @return All child tags that correspond to TagPath.
  */
 public XMLTag[] getRealTags(String TagPath) {
   if (TagPath.startsWith("/")) {
     return getRootTag().getTags(TagPath.substring(1));
   }
   if (TagPath.trim().equals("")) {
     return getTags();
   }
   String[] TempStrings = TagPath.split("/", 2);
   if (TempStrings.length == 1) {
     if (TagPath.equals(".") || (this instanceof XMLFile && TagPath.equals(TagName))) {
       return new XMLTag[] {this};
     } else if (TagPath.equals("..")) {
       if (ParentTag != null) {
         return new XMLTag[] {ParentTag};
       } else {
         return new XMLTag[0];
       }
     } else if (TagPath.equals("*")) {
       return (XMLTag[]) ChildTags.toArray(new XMLTag[0]);
     }
     ArrayList TemporaryStore = new ArrayList();
     // XMLTag[] Result = new XMLTag[0];
     for (int i = 0; i < ChildTags.size(); i++) {
       XMLTag TempTag = (XMLTag) ChildTags.get(i);
       if (!TempTag.isTemporary()) {
         if (TempTag.getName().equals(TagPath)) {
           TemporaryStore.add(TempTag);
         }
       }
     }
     return (XMLTag[]) TemporaryStore.toArray(new XMLTag[0]);
   } else {
     XMLTag[] Result = getTags(TempStrings[0]);
     ArrayList TemporaryStore = new ArrayList();
     for (int i = 0; i < Result.length; i++) {
       TemporaryStore.addAll(Arrays.asList(Result[i].getTags(TempStrings[1])));
     }
     return (XMLTag[]) TemporaryStore.toArray(new XMLTag[0]);
   }
 }
 public static void sortTagList(XMLTag list, String tagName, String criteria) {
   XMLTag[] unorderedVars = list.getTags(tagName);
   XMLTag[] orderedVars = XMLTag.sortTags(unorderedVars, criteria);
   for (int k = 0; k < unorderedVars.length; k++) unorderedVars[k].removeFromParent();
   for (int k = 0; k < orderedVars.length; k++) list.addTag(orderedVars[k]);
 }
 public int compareTo(Object OtherObject) {
   XMLTag ComparisonObject = (XMLTag) OtherObject;
   return TagName.compareTo(ComparisonObject.getName());
 }
 /**
  * Makes NewParent the parent tag of this.
  *
  * @param NewParent The tag to add this one to.
  * @return NewParent.
  */
 public XMLTag addToTag(XMLTag NewParent) {
   NewParent.addTag(this);
   return NewParent;
 }