001package com.ganteater.ae.util.xml.easyparser;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Map;
006import java.util.Set;
007import java.util.StringTokenizer;
008import java.util.Vector;
009
010import org.apache.commons.collections.map.LinkedMap;
011import org.apache.commons.lang.ObjectUtils;
012import org.apache.commons.lang.StringEscapeUtils;
013import org.apache.commons.lang.StringUtils;
014
015/**
016 * @author victort
017 */
018public class Node extends ArrayList<Node> {
019
020        private static final String INDENT = "\t";
021
022        private static final long serialVersionUID = 1L;
023
024        public static final String TEXT_TEAG_NAME = "$Text";
025
026        public static final String TAG_ID = "$id";
027
028        final static boolean debug = false;
029
030        protected int MAXATTRINLINE = 6;
031
032        protected final int SAMPLENODE = 0;
033
034        protected final int TEXNode = 1;
035
036        private String fTagName;
037
038        private Node fOwnerWriter = null;
039
040        private LinkedMap attributes = new LinkedMap();
041
042        private boolean fNill = true;
043
044        private String fText;
045
046        private boolean fIgnoreSpaceInText = false;
047
048        public Node(String aTagName) {
049                fTagName = aTagName;
050        }
051
052        public Node(String tagName, Map<String, String> attributes, List<Node> nodes) {
053                this(tagName);
054                fTagName = tagName;
055                if (attributes != null) {
056                        this.attributes = new LinkedMap(attributes);
057                }
058                if (nodes != null) {
059                        for (int i = 0; i < nodes.size(); i++) {
060                                Node theNode = (Node) nodes.get(i);
061                                addInnerTag(theNode);
062                        }
063                }
064
065        }
066
067        public boolean isNill() {
068                return fNill;
069        }
070
071        public void setNill(boolean aNill) {
072                fNill = aNill;
073                if (aNill) {
074                        clear();
075                }
076        }
077
078        @SuppressWarnings("unchecked")
079        public Map<String, String> getAttributes() {
080                return attributes;
081        }
082
083        public String getAttribute(String aName) {
084                return (String) attributes.get(aName);
085        }
086
087        public void setAttribute(String aName, String aValue) {
088                if (aName != null && aName.length() > 0 && Character.isLetter(aName.charAt(0))) {
089                        attributes.put(aName, aValue);
090                }
091        }
092
093        public void setAttribute(String aName, boolean aValue) {
094                attributes.put(aName, aValue ? "true" : "false");
095        }
096
097        public void setAttribute(String aName, long aValue) {
098                attributes.put(aName, Long.toString(aValue));
099        }
100
101        public void setAttributes(Map<String, String> aAttributes) {
102                attributes = new LinkedMap(aAttributes);
103        }
104
105        public void setAttribute(String aName, double aValue) {
106                attributes.put(aName, Double.toString(aValue));
107        }
108
109        public void removeAttribute(String name) {
110                attributes.remove(name);
111        }
112
113        public void removeAttributes() {
114                attributes.clear();
115        }
116
117        protected void addInnerTag(Node aNode) {
118                if (aNode == this) {
119                        throw new RuntimeException("Self inserting node.");
120                }
121                aNode.setOwner(this);
122                add(aNode);
123        }
124
125        protected void setOwner(Node aOwnerWriter) {
126                fOwnerWriter = aOwnerWriter;
127        }
128
129        protected int getLevel() {
130                if (fOwnerWriter == null) {
131                        return 0;
132                }
133                return fOwnerWriter.getLevel() + 1;
134        }
135
136        private String getAttributeLine(int aLevel, String aKey) {
137                StringBuffer theBuffer = new StringBuffer();
138
139                if (attributes.size() > MAXATTRINLINE) {
140                        theBuffer.append(getFormatingTab(aLevel));
141                } else {
142                        theBuffer.append(' ');
143                }
144
145                theBuffer.append(aKey);
146                theBuffer.append("=\"");
147
148                String theValueOut = (String) attributes.get(aKey);
149                theValueOut = replace(theValueOut);
150
151                theBuffer.append(theValueOut);
152                theBuffer.append('\"');
153
154                return theBuffer.toString();
155        }
156
157        public static String replace(String aValueOut) {
158                if (aValueOut != null) {
159                        // aValueOut = EasyParser.replaceProperties(aValueOut, "\"", "&quot;");
160                        // aValueOut = EasyParser.replaceProperties(aValueOut, "\'",
161                        // "&apos;");
162                        aValueOut = EasyParser.replaceProperties(aValueOut, "<", "&lt;");
163                        aValueOut = EasyParser.replaceProperties(aValueOut, ">", "&gt;");
164                }
165                return aValueOut;
166        }
167
168        public String getXMLText() {
169                if (fText != null) {
170                        return fText;
171                }
172                if (TEXT_TEAG_NAME.equals(fTagName)) {
173                        return new String();
174                }
175
176                if (debug) {
177                        System.out.println("-- getXMLText()");
178                }
179
180                String text = getXMLText(0);
181
182                return text;
183        }
184
185        protected String getXMLText(int aLevel) {
186                if (fText != null) {
187                        return getXMLText();
188                }
189                if (TEXT_TEAG_NAME.equals(fTagName)) {
190                        return new String();
191                }
192
193                if (debug) {
194                        System.out.println("-- getXMLText( int aLevel )");
195                }
196
197                aLevel++;
198
199                StringBuffer theBuffer = new StringBuffer();
200
201                theBuffer.append(getFormatingTab(aLevel));
202
203                if ("Comment".equals(fTagName) == false) {
204                        theBuffer.append('<');
205                        theBuffer.append(fTagName);
206
207                        if (attributes.size() > 0) {
208                                @SuppressWarnings("unchecked")
209                                Set<String> theEnumKey = attributes.keySet();
210
211                                for (String name : theEnumKey) {
212                                        if (attributes.size() > MAXATTRINLINE) {
213                                                theBuffer.append("\r\n");
214                                        }
215                                        theBuffer.append(getAttributeLine(aLevel, name));
216                                }
217                        }
218
219                        if (size() == 0) {
220                                theBuffer.append(" />");
221                        } else {
222                                theBuffer.append('>');
223
224                                if ((size() > 0 || attributes.size() > MAXATTRINLINE)
225                                                && TEXT_TEAG_NAME.equals(getNode(0).getTag()) == false) {
226                                        theBuffer.append("\r\n");
227                                }
228
229                                for (int theCurrent = 0; theCurrent < size(); theCurrent++) {
230                                        if (debug) {
231                                                System.out.println("-- getXMLText( int aLevel ) for ( int theCurrent... ");
232                                        }
233                                        theBuffer.append(getInnerXMLText(aLevel, theCurrent));
234                                }
235
236                                if (TEXT_TEAG_NAME.equals(getNode(0).getTag()) == false) {
237                                        theBuffer.append(getFormatingTab(aLevel));
238                                }
239
240                                theBuffer.append("</");
241                                theBuffer.append(fTagName);
242                                theBuffer.append('>');
243                        }
244                } else {
245                        theBuffer.append("<!--");
246                        for (int theCurrent = 0; theCurrent < size(); theCurrent++) {
247                                theBuffer.append(getInnerXMLText(aLevel, theCurrent).replaceAll("&lt;", "<").replaceAll("&gt;", ">")
248                                                .replaceAll("&amp;", "&").replaceAll("&quot;", "\"").replaceAll("&apos;", "\'"));
249                        }
250                        theBuffer.append("-->");
251                }
252
253                theBuffer.append(" \r\n");
254                return theBuffer.toString();
255        }
256
257        private String getFormatingTab(int aLevel) {
258                StringBuffer theBuffer = new StringBuffer();
259                for (int theTab = 1; theTab < aLevel; theTab++) {
260                        theBuffer.append(INDENT);
261                }
262                return theBuffer.toString();
263        }
264
265        private String getInnerXMLText(int aLevel, int theInnerTag) {
266                Node theNode = (Node) get(theInnerTag);
267                return theNode.getXMLText(aLevel);
268        }
269
270        public String getTag() {
271                return fTagName;
272        }
273
274        public Node[] getNodes(String aTag) {
275                if (aTag.indexOf("*/") == 0) {
276                        return new Node[] { findNode(aTag.substring(2), null, null) };
277                }
278
279                if (aTag.indexOf("/") > 0) {
280                        return getNodesPatch(aTag);
281                }
282
283                int theNumberNode = size();
284
285                List<Node> theResultArrayList = new ArrayList<>();
286
287                for (int i = 0; i < theNumberNode; i++) {
288                        Node theCurrenNode = (Node) get(i);
289                        String theTagName = theCurrenNode.getTag();
290                        int theLengPreffix = theTagName.indexOf(':');
291                        if (theLengPreffix >= 0) {
292                                theTagName = theTagName.substring(theLengPreffix + 1);
293                        }
294                        if (theTagName.equals(aTag)) {
295                                theResultArrayList.add(theCurrenNode);
296                        }
297                }
298
299                Node[] theResult = new Node[theResultArrayList.size()];
300                for (int i = 0; i < theResultArrayList.size(); i++) {
301                        theResult[i] = (Node) theResultArrayList.get(i);
302                }
303
304                return theResult;
305        }
306
307        private Node[] getNodesPatch(String aPatch) {
308                StringTokenizer theStringTokenizer = new StringTokenizer(aPatch, "/");
309
310                Node theCurrenNode = this;
311                Node[] theResult = null;
312
313                while (theStringTokenizer.hasMoreTokens()) {
314                        theResult = theCurrenNode.getNodes(theStringTokenizer.nextToken());
315                        if (theResult != null && theResult.length > 0) {
316                                theCurrenNode = theResult[0];
317                        }
318                }
319
320                return theResult;
321        }
322
323        public void setText(String aText) {
324
325                aText = replace(aText);
326
327                if (fIgnoreSpaceInText) {
328                        StringBuffer theBuffer = null;
329                        StringTokenizer theTokenizer = new StringTokenizer(aText, " \n\r");
330                        theBuffer = new StringBuffer();
331                        boolean theFirstToken = true;
332                        while (theTokenizer.hasMoreTokens()) {
333                                if (theFirstToken) {
334                                        theFirstToken = false;
335                                } else {
336                                        theBuffer.append(' ');
337                                }
338
339                                theBuffer.append(theTokenizer.nextToken());
340                        }
341                        fText = theBuffer.toString();
342                } else {
343                        fText = aText;
344                }
345        }
346
347        public String getText() {
348                return StringEscapeUtils.unescapeXml(fText);
349        }
350
351        public Node findNode(String aTagName, String aAttributeName, String aAttributeValue) {
352                int theNumberNode = size();
353
354                String tag = getTag();
355                int prefixpoint = tag.indexOf(':');
356                if (prefixpoint >= 0) {
357                        tag = tag.substring(prefixpoint + 1);
358                }
359
360                String theAttribut = null;
361                if (aAttributeName != null) {
362                        theAttribut = getAttribute(aAttributeName);
363                }
364
365                if (aTagName == null || tag.equals(aTagName)) {
366                        if (aAttributeName != null) {
367                                if (ObjectUtils.equals(theAttribut, aAttributeValue)) {
368                                        return this;
369                                }
370                        } else {
371                                return this;
372                        }
373                }
374
375                for (int i = 0; i < theNumberNode; i++) {
376                        Node theCurrenNode = (Node) get(i);
377                        Node node = theCurrenNode.findNode(aTagName, aAttributeName, aAttributeValue);
378                        if (node != null) {
379                                return node;
380                        }
381                }
382
383                return null;
384        }
385
386        public Node[] findNodes(String tagName, String attributeName, String attributeValue) {
387                int theNumberNode = size();
388
389                Vector<Node> noresVector = new Vector<>();
390
391                for (int i = 0; i < theNumberNode; i++) {
392                        Node node = (Node) get(i);
393
394                        String value = node.getAttribute(attributeName);
395
396                        if (StringUtils.equals(node.getTag(), tagName)
397                                        && (StringUtils.equals(value, attributeValue) || attributeName == null)) {
398                                noresVector.add(node);
399                        }
400
401                        Node[] nodes = node.findNodes(tagName, attributeName, attributeValue);
402                        for (int j = 0; j < nodes.length; j++) {
403                                noresVector.add(nodes[j]);
404                        }
405                }
406
407                Node[] result = new Node[noresVector.size()];
408                for (int i = 0; i < result.length; i++) {
409                        result[i] = (Node) noresVector.get(i);
410                }
411
412                return result;
413        }
414
415        public void setTag(String aTagName) {
416                fTagName = aTagName;
417        }
418
419        public Node getNode(int i) {
420                return (Node) get(i);
421        }
422
423        public void removeNode(String theAttributeName, String theAttributeValue) {
424                removeNode(this, theAttributeName, theAttributeValue);
425        }
426
427        private void removeNode(Node aNode, String theAttributeName, String theAttributeValue) {
428                for (int i = 0; i < aNode.size(); i++) {
429                        Node theCurrentNode = (Node) aNode.get(i);
430                        String theAttribut = theCurrentNode.getAttribute(theAttributeName);
431                        if (theAttribut != null && theAttribut.equals(theAttributeValue)) {
432                                aNode.remove(i);
433                                return;
434                        } else {
435                                removeNode(theCurrentNode, theAttributeName, theAttributeValue);
436                        }
437                }
438        }
439
440        class TagCounter {
441                public int number = 0;
442
443                public int getNumber() {
444                        return number++;
445                }
446        }
447
448        public TreeVector getVector() {
449                TreeVector o = (TreeVector) nodeHashtables(this, null, false);
450                if (o == null) {
451                        o = new TreeVector();
452                }
453
454                return o;
455        }
456
457        public TreeVector getVector(boolean withId) {
458                TreeVector hashtable = new TreeVector();
459                TagCounter numberCommand = null;
460                if (withId)
461                        numberCommand = new TagCounter();
462
463                Object o = nodeHashtables(this, numberCommand, withId);
464                if (o == null) {
465                        o = new Vector<Object>();
466                }
467                hashtable.add(o);
468
469                return hashtable;
470        }
471
472        private Object nodeHashtables(Node aNode, TagCounter commandId, boolean removeId) {
473                TreeVector hashtable = new TreeVector();
474
475                Node key = aNode;
476
477                int number = -1;
478
479                if (commandId != null)
480                        number = commandId.getNumber();
481
482                if (TEXT_TEAG_NAME.equals(aNode.getTag()) == false) {
483                        if (number > 0)
484                                key.setAttribute(TAG_ID, number);
485
486                        Node node = new Node(aNode.getTag());
487                        @SuppressWarnings("unchecked")
488                        Map<String, String> attributes = new LinkedMap(aNode.getAttributes());
489
490                        if (removeId) {
491                                attributes.remove(TAG_ID);
492                        }
493
494                        node.setAttributes(attributes);
495                        String s = node.getXMLText().replace('\n', ' ').trim();
496                        if (s.length() > 0) {
497                                s = s.substring(1, s.length() - 3);
498                        }
499
500                        hashtable.setNode(aNode);
501                        hashtable.setTitle(s);
502
503                        int numberNodes = aNode.size();
504
505                        for (int i = 0; i < numberNodes; i++) {
506                                hashtable.addElement(nodeHashtables((Node) aNode.get(i), commandId, removeId));
507                        }
508
509                } else {
510                        String text = aNode.getText();
511
512                        if (text != null) {
513                                return text;
514                        } else {
515                                return '<' + aNode.getTag() + "/>";
516                        }
517                }
518
519                return hashtable;
520        }
521
522        public static class TreeVector extends Vector<Object> {
523                private static final long serialVersionUID = 1L;
524
525                private String fTitle;
526                private Node node;
527
528                public TreeVector(String text) {
529                        fTitle = text;
530                }
531
532                public TreeVector() {
533                }
534
535                public synchronized String toString() {
536                        return fTitle;
537                }
538
539                public void setNode(Node node) {
540                        this.node = node;
541                }
542
543                public void setTitle(String title) {
544                        fTitle = title;
545                }
546
547                public Node getNode() {
548                        return node;
549                }
550        }
551
552        @Override
553        public String toString() {
554                return getXMLText();
555        }
556
557        public Node[] getTextNodes() {
558                return getNodes(TEXT_TEAG_NAME);
559        }
560
561        public String getInnerText() {
562                Node[] nodes = getTextNodes();
563                if (nodes.length > 0) {
564                        return nodes[0].getText();
565                }
566                return StringUtils.EMPTY;
567        }
568
569        public String getInnerXMLText() {
570                StringBuilder xml = new StringBuilder();
571                for (Node node : this) {
572                        xml.append(node.getXMLText());
573                        xml.append('\n');
574                }
575                return xml.toString();
576        }
577
578        @Override
579        public Node clone() {
580                Node clone = new Node(getTag());
581                clone.setAttributes(getAttributes());
582                clone.addAll(this);
583                return clone;
584        }
585}