Source Code Cross Referenced for Element.java in  » XML » xom » nu » xom » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » XML » xom » nu.xom 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* Copyright 2002-2005 Elliotte Rusty Harold
0002:           
0003:           This library is free software; you can redistribute it and/or modify
0004:           it under the terms of version 2.1 of the GNU Lesser General Public 
0005:           License as published by the Free Software Foundation.
0006:           
0007:           This library is distributed in the hope that it will be useful,
0008:           but WITHOUT ANY WARRANTY; without even the implied warranty of
0009:           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
0010:           GNU Lesser General Public License for more details.
0011:           
0012:           You should have received a copy of the GNU Lesser General Public
0013:           License along with this library; if not, write to the 
0014:           Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
0015:           Boston, MA 02111-1307  USA
0016:           
0017:           You can contact Elliotte Rusty Harold by sending e-mail to
0018:           elharo@metalab.unc.edu. Please include the word "XOM" in the
0019:           subject line. The XOM home page is located at http://www.xom.nu/
0020:         */
0021:
0022:        package nu.xom;
0023:
0024:        import java.util.HashMap;
0025:        import java.util.HashSet;
0026:        import java.util.Iterator;
0027:        import java.util.Map;
0028:        import java.util.NoSuchElementException; // ???? Why do I still need these imports? Could 
0029:        // I get rid of them? In particular could I get rid of sorting requirement on sets?
0030:        import java.util.Set;
0031:        import java.util.SortedSet;
0032:        import java.util.TreeSet;
0033:
0034:        /**
0035:         * <p>
0036:         * This class represents an XML element. Each 
0037:         * element has the following properties:
0038:         * </p>
0039:         * 
0040:         * <ul>
0041:         *   <li>Local name</li>
0042:         *   <li>Prefix (which may be null or the empty string) </li>
0043:         *   <li>Namespace URI (which may be null or the empty string) </li>
0044:         *   <li>A list of attributes</li>
0045:         *   <li>A list of namespace declarations for this element
0046:         *       (not including those inherited from its parent)</li>
0047:         *   <li>A list of child nodes</li>
0048:         * </ul>
0049:         * 
0050:         * @author Elliotte Rusty Harold
0051:         * @version 1.1b3
0052:         *
0053:         */
0054:        public class Element extends ParentNode {
0055:
0056:            private String localName;
0057:            private String prefix;
0058:            private String URI;
0059:
0060:            private Attribute[] attributes = null;
0061:            private int numAttributes = 0;
0062:            private Namespaces namespaces = null;
0063:
0064:            /**
0065:             * <p>
0066:             * Creates a new element in no namespace.
0067:             * </p>
0068:             * 
0069:             * @param name the name of the element
0070:             * 
0071:             * @throws IllegalNameException if <code>name</code>
0072:             *     is not a legal XML 1.0 non-colonized name
0073:             */
0074:            public Element(String name) {
0075:                this (name, "");
0076:            }
0077:
0078:            /**
0079:             * <p>
0080:             * Creates a new element in a namespace.
0081:             * </p>
0082:             * 
0083:             * @param name the qualified name of the element
0084:             * @param uri the namespace URI of the element
0085:             * 
0086:             * @throws IllegalNameException if <code>name</code>  
0087:             *     is not a legal XML 1.0 name
0088:             * @throws NamespaceConflictException if <code>name</code>'s prefix  
0089:             *     cannot be used with <code>uri</code>
0090:             * @throws MalformedURIException if <code>uri</code>  
0091:             *     is not an RFC 3986 absolute URI reference
0092:             */
0093:            public Element(String name, String uri) {
0094:
0095:                // The shadowing is important here.
0096:                // I don't want to set the prefix field just yet.
0097:                String prefix = "";
0098:                String localName = name;
0099:                int colon = name.indexOf(':');
0100:                if (colon > 0) {
0101:                    prefix = name.substring(0, colon);
0102:                    localName = name.substring(colon + 1);
0103:                }
0104:
0105:                // The order of these next two calls
0106:                // matters a great deal.
0107:                _setNamespacePrefix(prefix);
0108:                _setNamespaceURI(uri);
0109:                try {
0110:                    _setLocalName(localName);
0111:                } catch (IllegalNameException ex) {
0112:                    ex.setData(name);
0113:                    throw ex;
0114:                }
0115:
0116:            }
0117:
0118:            private Element() {
0119:            }
0120:
0121:            static Element build(String name, String uri, String localName) {
0122:
0123:                Element result = new Element();
0124:                String prefix = "";
0125:                int colon = name.indexOf(':');
0126:                if (colon >= 0) {
0127:                    prefix = name.substring(0, colon);
0128:                }
0129:                result.prefix = prefix;
0130:                result.localName = localName;
0131:                // We do need to verify the URI here because parsers are 
0132:                // allowing relative URIs which XOM forbids, for reasons
0133:                // of canonical XML if nothing else. But we only have to verify
0134:                // that it's an absolute base URI. I don't have to verify 
0135:                // no conflicts.
0136:                if (!"".equals(uri))
0137:                    Verifier.checkAbsoluteURIReference(uri);
0138:                result.URI = uri;
0139:                return result;
0140:
0141:            }
0142:
0143:            /**
0144:             * <p>
0145:             * Creates a deep copy of an element.
0146:             * The copy is disconnected from the tree, and does not
0147:             * have a parent.
0148:             * </p>
0149:             * 
0150:             * @param element the element to copy
0151:             * 
0152:             */
0153:            public Element(Element element) {
0154:
0155:                this .prefix = element.prefix;
0156:                this .localName = element.localName;
0157:                this .URI = element.URI;
0158:
0159:                // Attach additional namespaces
0160:                if (element.namespaces != null) {
0161:                    this .namespaces = element.namespaces.copy();
0162:                }
0163:
0164:                // Attach clones of attributes
0165:                if (element.attributes != null) {
0166:                    this .attributes = element.copyAttributes(this );
0167:                    this .numAttributes = element.numAttributes;
0168:                }
0169:
0170:                this .actualBaseURI = element.findActualBaseURI();
0171:
0172:                copyChildren(element, this );
0173:
0174:            }
0175:
0176:            private Attribute[] copyAttributes(Element newParent) {
0177:
0178:                Attribute[] copy = new Attribute[numAttributes];
0179:                for (int i = 0; i < numAttributes; i++) {
0180:                    copy[i] = (Attribute) attributes[i].copy();
0181:                    copy[i].setParent(newParent);
0182:                }
0183:                return copy;
0184:
0185:            }
0186:
0187:            private static Element copyTag(final Element source) {
0188:
0189:                Element result = source.shallowCopy();
0190:
0191:                // Attach additional namespaces
0192:                if (source.namespaces != null) {
0193:                    result.namespaces = source.namespaces.copy();
0194:                }
0195:
0196:                // Attach clones of attributes
0197:                if (source.attributes != null) {
0198:                    result.attributes = source.copyAttributes(result);
0199:                    result.numAttributes = source.numAttributes;
0200:                }
0201:
0202:                result.actualBaseURI = source.findActualBaseURI();
0203:
0204:                return result;
0205:
0206:            }
0207:
0208:            private static void copyChildren(final Element sourceElement,
0209:                    Element resultElement) {
0210:
0211:                if (sourceElement.getChildCount() == 0)
0212:                    return;
0213:                ParentNode resultParent = resultElement;
0214:                Node sourceCurrent = sourceElement;
0215:                int index = 0;
0216:                int[] indexes = new int[10];
0217:                int top = 0;
0218:                indexes[0] = 0;
0219:
0220:                // true if processing the element for the 2nd time; 
0221:                // i.e. the element's end-tag
0222:                boolean endTag = false;
0223:
0224:                while (true) {
0225:                    if (!endTag && sourceCurrent.getChildCount() > 0) {
0226:                        sourceCurrent = sourceCurrent.getChild(0);
0227:                        index = 0;
0228:                        top++;
0229:                        indexes = grow(indexes, top);
0230:                        indexes[top] = 0;
0231:                    } else {
0232:                        endTag = false;
0233:                        ParentNode sourceParent = sourceCurrent.getParent();
0234:                        if (sourceParent.getChildCount() - 1 == index) {
0235:                            sourceCurrent = sourceParent;
0236:                            top--;
0237:                            if (sourceCurrent == sourceElement)
0238:                                break;
0239:                            // switch parent up
0240:                            resultParent = (Element) resultParent.getParent();
0241:                            index = indexes[top];
0242:                            endTag = true;
0243:                            continue;
0244:                        } else {
0245:                            index++;
0246:                            indexes[top] = index;
0247:                            sourceCurrent = sourceParent.getChild(index);
0248:                        }
0249:                    }
0250:
0251:                    if (sourceCurrent.isElement()) {
0252:                        Element child = copyTag((Element) sourceCurrent);
0253:                        resultParent.appendChild(child);
0254:                        if (sourceCurrent.getChildCount() > 0) {
0255:                            resultParent = child;
0256:                        }
0257:                    } else {
0258:                        Node child = sourceCurrent.copy();
0259:                        resultParent.appendChild(child);
0260:                    }
0261:
0262:                }
0263:
0264:            }
0265:
0266:            private static int[] grow(int[] indexes, int top) {
0267:
0268:                if (top < indexes.length)
0269:                    return indexes;
0270:                int[] result = new int[indexes.length * 2];
0271:                System.arraycopy(indexes, 0, result, 0, indexes.length);
0272:                return result;
0273:
0274:            }
0275:
0276:            /**
0277:             * <p>
0278:             * Returns a list of the child elements of 
0279:             * this element with the specified name in no namespace.
0280:             * The elements returned are in document order.
0281:             * </p>
0282:             * 
0283:             * @param name the name of the elements included in the list
0284:             * 
0285:             * @return a comatose list containing the child elements of this 
0286:             *     element with the specified name
0287:             */
0288:            public final Elements getChildElements(String name) {
0289:                return getChildElements(name, "");
0290:            }
0291:
0292:            /**
0293:             * <p>
0294:             * Returns a list of the immediate child elements of this 
0295:             * element with the specified local name and namespace URI.
0296:             * Passing the empty string or null as the local name
0297:             * returns all elements in the specified namespace.
0298:             * Passing null or the empty string as the namespace URI 
0299:             * returns elements with the specified name in no namespace.
0300:             * The elements returned are in document order.
0301:             * </p>
0302:             * 
0303:             * @param localName the name of the elements included in the list
0304:             * @param namespaceURI the namespace URI of the elements included
0305:             *     in the list
0306:             * 
0307:             * @return a comatose list containing the child  
0308:             *    elements of this element with the specified
0309:             *    name in the specified namespace
0310:             */
0311:            public final Elements getChildElements(String localName,
0312:                    String namespaceURI) {
0313:
0314:                if (namespaceURI == null)
0315:                    namespaceURI = "";
0316:                if (localName == null)
0317:                    localName = "";
0318:
0319:                Elements elements = new Elements();
0320:                for (int i = 0; i < getChildCount(); i++) {
0321:                    Node child = getChild(i);
0322:                    if (child.isElement()) {
0323:                        Element element = (Element) child;
0324:                        if ((localName.equals(element.getLocalName()) || localName
0325:                                .length() == 0)
0326:                                && namespaceURI.equals(element
0327:                                        .getNamespaceURI())) {
0328:                            elements.add(element);
0329:                        }
0330:                    }
0331:                }
0332:                return elements;
0333:
0334:            }
0335:
0336:            /**
0337:             * <p>
0338:             * Returns a list of all the child elements 
0339:             * of this element in document order.
0340:             * </p>
0341:             * 
0342:             * @return a comatose list containing all  
0343:             *    child elements of this element
0344:             */
0345:            public final Elements getChildElements() {
0346:
0347:                Elements elements = new Elements();
0348:                for (int i = 0; i < getChildCount(); i++) {
0349:                    Node child = getChild(i);
0350:                    if (child.isElement()) {
0351:                        Element element = (Element) child;
0352:                        elements.add(element);
0353:                    }
0354:                }
0355:                return elements;
0356:
0357:            }
0358:
0359:            /**
0360:             * <p>
0361:             * Returns the first child
0362:             * element with the specified name in no namespace.
0363:             * If there is no such element, it returns null.
0364:             * </p>
0365:             * 
0366:             * @param name the name of the element to return
0367:             * 
0368:             * @return the first child element with the specified local name 
0369:             *    in no namespace or null if there is no such element
0370:             */
0371:            public final Element getFirstChildElement(String name) {
0372:                return getFirstChildElement(name, "");
0373:            }
0374:
0375:            /**
0376:             * <p>
0377:             * Returns the first child
0378:             * element with the specified local name and namespace URI.
0379:             * If there is no such element, it returns null.
0380:             * </p>
0381:             * 
0382:             * @param localName the local name of the element to return
0383:             * @param namespaceURI the namespace URI of the element to return
0384:             * 
0385:             * @return the first child with the specified local name in the
0386:             *      specified namespace, or null if there is no such element
0387:             */
0388:            public final Element getFirstChildElement(String localName,
0389:                    String namespaceURI) {
0390:
0391:                for (int i = 0; i < getChildCount(); i++) {
0392:                    Node child = getChild(i);
0393:                    if (child.isElement()) {
0394:                        Element element = (Element) child;
0395:                        if (localName.equals(element.getLocalName())
0396:                                && namespaceURI.equals(element
0397:                                        .getNamespaceURI())) {
0398:                            return element;
0399:                        }
0400:                    }
0401:                }
0402:                return null;
0403:
0404:            }
0405:
0406:            /**
0407:             * <p>
0408:             * Adds an attribute to this element, replacing any existing 
0409:             * attribute with the same local name and namespace URI.
0410:             * </p>
0411:             * 
0412:             * @param attribute the attribute to add
0413:             * 
0414:             * @throws MultipleParentException if the attribute is already
0415:             *      attached to an element
0416:             * @throws NamespaceConflictException if the attribute's prefix   
0417:             *      is mapped to a different namespace URI than the same prefix
0418:             *      is mapped to by this element, another attribute of 
0419:             *      this element, or an additional namespace declaration
0420:             *      of this element
0421:             */
0422:            public void addAttribute(Attribute attribute) {
0423:
0424:                if (attribute.getParent() != null) {
0425:                    throw new MultipleParentException(
0426:                            "Attribute already has a parent");
0427:                }
0428:
0429:                // check for namespace conflicts
0430:                String attPrefix = attribute.getNamespacePrefix();
0431:                if (attPrefix.length() != 0 && !"xml".equals(attPrefix)) {
0432:                    if (prefix.equals(attribute.getNamespacePrefix())
0433:                            && !(getNamespaceURI().equals(attribute
0434:                                    .getNamespaceURI()))) {
0435:                        throw new NamespaceConflictException("Prefix of "
0436:                                + attribute.getQualifiedName()
0437:                                + " conflicts with element prefix " + prefix);
0438:                    }
0439:                    // check for conflicts with additional namespaces
0440:                    if (namespaces != null) {
0441:                        String existing = namespaces.getURI(attribute
0442:                                .getNamespacePrefix());
0443:                        if (existing != null
0444:                                && !existing
0445:                                        .equals(attribute.getNamespaceURI())) {
0446:                            throw new NamespaceConflictException(
0447:                                    "Attribute prefix  "
0448:                                            + attPrefix
0449:                                            + " conflicts with namespace declaration.");
0450:                        }
0451:                    }
0452:
0453:                }
0454:
0455:                if (attributes == null)
0456:                    attributes = new Attribute[1];
0457:                checkPrefixConflict(attribute);
0458:
0459:                // Is there already an attribute with this local name
0460:                // and namespace? If so, remove it.
0461:                Attribute oldAttribute = getAttribute(attribute.getLocalName(),
0462:                        attribute.getNamespaceURI());
0463:                if (oldAttribute != null)
0464:                    remove(oldAttribute);
0465:
0466:                add(attribute);
0467:                attribute.setParent(this );
0468:
0469:            }
0470:
0471:            private void add(Attribute attribute) {
0472:
0473:                if (numAttributes == attributes.length) {
0474:                    Attribute[] newAttributes = new Attribute[attributes.length * 2];
0475:                    System.arraycopy(attributes, 0, newAttributes, 0,
0476:                            numAttributes);
0477:                    this .attributes = newAttributes;
0478:                }
0479:                attributes[numAttributes] = attribute;
0480:                numAttributes++;
0481:
0482:            }
0483:
0484:            private boolean remove(Attribute attribute) {
0485:
0486:                int index = -1;
0487:                for (int i = 0; i < attributes.length; i++) {
0488:                    if (attributes[i] == attribute) {
0489:                        index = i;
0490:                        break;
0491:                    }
0492:                }
0493:
0494:                if (index == -1)
0495:                    return false;
0496:
0497:                int toCopy = numAttributes - index - 1;
0498:                if (toCopy > 0) {
0499:                    System.arraycopy(attributes, index + 1, attributes, index,
0500:                            toCopy);
0501:                }
0502:                numAttributes--;
0503:                attributes[numAttributes] = null;
0504:                return true;
0505:
0506:            }
0507:
0508:            void fastAddAttribute(Attribute attribute) {
0509:                if (attributes == null)
0510:                    attributes = new Attribute[1];
0511:                add(attribute);
0512:                attribute.setParent(this );
0513:            }
0514:
0515:            /**
0516:             * <p>
0517:             * Removes an attribute from this element.
0518:             * </p>
0519:             * 
0520:             * @param attribute the attribute to remove
0521:             * 
0522:             * @return the attribute that was removed
0523:             * 
0524:             * @throws NoSuchAttributeException if this element is not the  
0525:             *     parent of attribute
0526:             * 
0527:             */
0528:            public Attribute removeAttribute(Attribute attribute) {
0529:
0530:                if (attributes == null) {
0531:                    throw new NoSuchAttributeException(
0532:                            "Tried to remove attribute "
0533:                                    + attribute.getQualifiedName()
0534:                                    + " from non-parent element");
0535:                }
0536:                if (attribute == null) {
0537:                    throw new NullPointerException(
0538:                            "Tried to remove null attribute");
0539:                }
0540:                if (remove(attribute)) {
0541:                    attribute.setParent(null);
0542:                    return attribute;
0543:                } else {
0544:                    throw new NoSuchAttributeException(
0545:                            "Tried to remove attribute "
0546:                                    + attribute.getQualifiedName()
0547:                                    + " from non-parent element");
0548:                }
0549:
0550:            }
0551:
0552:            /**
0553:             * <p>
0554:             * Returns the attribute with the specified name in no namespace,
0555:             * or null if this element does not have an attribute 
0556:             * with that name in no namespace.
0557:             * </p>
0558:             * 
0559:             * @param name the name of the attribute
0560:             * 
0561:             * @return the attribute of this element with the specified name
0562:             */
0563:            public final Attribute getAttribute(String name) {
0564:                return getAttribute(name, "");
0565:            }
0566:
0567:            /**
0568:             * <p>
0569:             * Returns the attribute with the specified name and namespace URI,
0570:             * or null if this element does not have an attribute 
0571:             * with that name in that namespace.
0572:             * </p>
0573:             * 
0574:             * @param localName the local name of the attribute
0575:             * @param namespaceURI the namespace of the attribute
0576:             * 
0577:             * @return the attribute of this element 
0578:             *     with the specified name and namespace
0579:             */
0580:            public final Attribute getAttribute(String localName,
0581:                    String namespaceURI) {
0582:
0583:                if (attributes == null)
0584:                    return null;
0585:                for (int i = 0; i < numAttributes; i++) {
0586:                    Attribute a = attributes[i];
0587:                    if (a.getLocalName().equals(localName)
0588:                            && a.getNamespaceURI().equals(namespaceURI)) {
0589:                        return a;
0590:                    }
0591:                }
0592:
0593:                return null;
0594:
0595:            }
0596:
0597:            /**
0598:             * <p>
0599:             * Returns the value of the attribute with the specified 
0600:             * name in no namespace,
0601:             * or null if this element does not have an attribute 
0602:             * with that name.
0603:             * </p>
0604:             * 
0605:             * @param name the name of the attribute
0606:             * 
0607:             * @return the value of the attribute of this element 
0608:             *     with the specified name
0609:             */
0610:            public final String getAttributeValue(String name) {
0611:                return getAttributeValue(name, "");
0612:            }
0613:
0614:            /**
0615:             * 
0616:             * <p>
0617:             * Returns the number of attributes of this <code>Element</code>,
0618:             * not counting namespace declarations.
0619:             * This is always a non-negative number.
0620:             * </p>
0621:             * 
0622:             * @return the number of attributes in the container
0623:             */
0624:            public final int getAttributeCount() {
0625:                return numAttributes;
0626:            }
0627:
0628:            /**
0629:             * 
0630:             * <p>
0631:             * Selects an attribute by index.
0632:             * The index is purely for convenience and has no particular 
0633:             * meaning. In particular, it is <em>not</em> necessarily the 
0634:             * position of this attribute in the original document from 
0635:             * which this <code>Element</code> object was read.
0636:             * As with most lists in Java, attributes are numbered  
0637:             * from 0 to one less than the length of the list.
0638:             * </p>
0639:             * 
0640:             * <p>
0641:             * In general, you should not add attributes to or remove 
0642:             * attributes from the list while iterating across it. 
0643:             * Doing so will change the indexes of the other attributes in 
0644:             * the list. it is, however, safe to remove an attribute from 
0645:             * either end of the list (0 or <code>getAttributeCount()-1</code>)
0646:             * until there are no attributes left. 
0647:             * </p>
0648:             * 
0649:             * @param index the attribute to return
0650:             * 
0651:             * @return the index<sup>th</sup> attribute of this element
0652:             * 
0653:             * @throws IndexOutofBoundsException if the index is negative 
0654:             *   or greater than or equal to the number of attributes 
0655:             *   of this element
0656:             * 
0657:             */
0658:            public final Attribute getAttribute(int index) {
0659:
0660:                if (attributes == null) {
0661:                    throw new IndexOutOfBoundsException(
0662:                            "Element does not have any attributes");
0663:                }
0664:                return attributes[index];
0665:
0666:            }
0667:
0668:            /**
0669:             * <p>
0670:             * Returns the value of the attribute with the 
0671:             * specified name and namespace URI,
0672:             * or null if this element does not have such an attribute.
0673:             * </p>
0674:             * 
0675:             * @param localName the name of the attribute
0676:             * @param namespaceURI the namespace of the attribute
0677:             * 
0678:             * @return the value of the attribute of this element 
0679:             *     with the specified name and namespace
0680:             */
0681:            public final String getAttributeValue(String localName,
0682:                    String namespaceURI) {
0683:
0684:                Attribute attribute = getAttribute(localName, namespaceURI);
0685:                if (attribute == null)
0686:                    return null;
0687:                else
0688:                    return attribute.getValue();
0689:
0690:            }
0691:
0692:            /**
0693:             * <p>
0694:             * Returns the local name of this element, not including the 
0695:             * namespace prefix or colon.
0696:             * </p>
0697:             * 
0698:             * @return the local name of this element
0699:             */
0700:            public final String getLocalName() {
0701:                return localName;
0702:            }
0703:
0704:            /**
0705:             * <p>
0706:             * Returns the complete name of this element, including the 
0707:             * namespace prefix if this element has one.
0708:             * </p>
0709:             * 
0710:             * @return the qualified name of this element
0711:             */
0712:            public final String getQualifiedName() {
0713:                if (prefix.length() == 0)
0714:                    return localName;
0715:                else
0716:                    return prefix + ":" + localName;
0717:            }
0718:
0719:            /**
0720:             * <p>
0721:             * Returns the prefix of this element, or the empty string
0722:             * if this element does not have a prefix.
0723:             * </p>
0724:             * 
0725:             * @return the prefix of this element
0726:             */
0727:            public final String getNamespacePrefix() {
0728:                return prefix;
0729:            }
0730:
0731:            /**
0732:             * <p>
0733:             * Returns the namespace URI of this element,
0734:             * or the empty string if this element is not
0735:             * in a namespace.
0736:             * </p>
0737:             * 
0738:             * @return  the namespace URI of this element
0739:             */
0740:            public final String getNamespaceURI() {
0741:                return URI;
0742:            }
0743:
0744:            /**
0745:             * <p>
0746:             * Returns the namespace URI mapped to the specified
0747:             * prefix within this element. Returns null if this prefix
0748:             * is not associated with a URI.
0749:             * </p>
0750:             * 
0751:             * @param prefix the namespace prefix whose URI is desired
0752:             *
0753:             * @return the namespace URI mapped to <code>prefix</code>
0754:             */
0755:            public final String getNamespaceURI(String prefix) {
0756:
0757:                Element current = this ;
0758:                String result = getLocalNamespaceURI(prefix);
0759:                while (result == null) {
0760:                    ParentNode parent = current.getParent();
0761:                    if (parent == null || parent.isDocument())
0762:                        break;
0763:                    current = (Element) parent;
0764:                    result = current.getLocalNamespaceURI(prefix);
0765:                }
0766:                if (result == null && "".equals(prefix))
0767:                    result = "";
0768:                return result;
0769:
0770:            }
0771:
0772:            final String getLocalNamespaceURI(String prefix) {
0773:
0774:                if (prefix.equals(this .prefix))
0775:                    return this .URI;
0776:
0777:                if ("xml".equals(prefix)) {
0778:                    return "http://www.w3.org/XML/1998/namespace";
0779:                }
0780:                // This next line uses the original Namespaces 1.0 
0781:                // specification rules.
0782:                // Namespaces 1.0 + errata is different
0783:                if ("xmlns".equals(prefix))
0784:                    return "";
0785:                // Look in the additional namespace declarations
0786:                if (namespaces != null) {
0787:                    String result = namespaces.getURI(prefix);
0788:                    if (result != null)
0789:                        return result;
0790:                }
0791:                // Look in the attributes
0792:                if (prefix.length() != 0 && attributes != null) {
0793:                    for (int i = 0; i < numAttributes; i++) {
0794:                        Attribute a = attributes[i];
0795:                        if (a.getNamespacePrefix().equals(prefix)) {
0796:                            return a.getNamespaceURI();
0797:                        }
0798:                    }
0799:                }
0800:
0801:                return null;
0802:
0803:            }
0804:
0805:            /**
0806:             * <p>
0807:             * Sets the local name of this element.
0808:             * </p>
0809:             * 
0810:             * @param localName the new local name
0811:             * 
0812:             * @throws IllegalNameException if <code>localName</code> is not
0813:             *     a legal, non-colonized name
0814:             */
0815:            public void setLocalName(String localName) {
0816:                _setLocalName(localName);
0817:            }
0818:
0819:            private void _setLocalName(String localName) {
0820:                Verifier.checkNCName(localName);
0821:                this .localName = localName;
0822:            }
0823:
0824:            /**
0825:             * <p>
0826:             * Sets the namespace URI of this element.
0827:             * </p>
0828:             * 
0829:             * @param uri the new namespace URI
0830:             * 
0831:             * @throws MalformedURIException if <code>uri</code>  
0832:             *     is not an absolute RFC 3986 URI reference
0833:             * @throws NamespaceException if this element has a prefix 
0834:             *     and <code>uri</code> is null or the empty string;
0835:             *     or if the element's prefix is shared by an attribute
0836:             *     or additional namespace
0837:             */
0838:            public void setNamespaceURI(String uri) {
0839:                _setNamespaceURI(uri);
0840:            }
0841:
0842:            private void _setNamespaceURI(String uri) {
0843:
0844:                if (uri == null)
0845:                    uri = "";
0846:                // Next line is needed to avoid unintentional
0847:                // exceptions below when checking for conflicts
0848:                if (uri.equals(this .URI))
0849:                    return;
0850:                if (uri.length() == 0) { // faster than "".equals(uri)
0851:                    if (prefix.length() != 0) {
0852:                        throw new NamespaceConflictException(
0853:                                "Prefixed elements must have namespace URIs.");
0854:                    }
0855:                } else
0856:                    Verifier.checkAbsoluteURIReference(uri);
0857:                // Make sure this doesn't conflict with any local
0858:                // attribute prefixes or additional namespace declarations
0859:                // Note that if the prefix equals the prefix, then the
0860:                // URI must equal the old URI, so the URI can't easily be
0861:                // changed. (you'd need to detach everything first;
0862:                // change the URIs, then put it all back together
0863:                if (namespaces != null) {
0864:                    String result = namespaces.getURI(prefix);
0865:                    if (result != null) {
0866:                        throw new NamespaceConflictException(
0867:                                "new URI conflicts with existing prefix");
0868:                    }
0869:                }
0870:                // Look in the attributes
0871:                if (uri.length() > 0 && attributes != null) {
0872:                    for (int i = 0; i < numAttributes; i++) {
0873:                        Attribute a = attributes[i];
0874:                        String attPrefix = a.getNamespacePrefix();
0875:                        if (attPrefix.length() == 0)
0876:                            continue;
0877:                        if (a.getNamespacePrefix().equals(prefix)) {
0878:                            throw new NamespaceConflictException(
0879:                                    "new element URI " + uri
0880:                                            + " conflicts with attribute "
0881:                                            + a.getQualifiedName());
0882:                        }
0883:                    }
0884:                }
0885:
0886:                if ("http://www.w3.org/XML/1998/namespace".equals(uri)
0887:                        && !"xml".equals(prefix)) {
0888:                    throw new NamespaceConflictException(
0889:                            "Wrong prefix "
0890:                                    + prefix
0891:                                    + " for the http://www.w3.org/XML/1998/namespace namespace URI");
0892:                } else if ("xml".equals(prefix)
0893:                        && !"http://www.w3.org/XML/1998/namespace".equals(uri)) {
0894:                    throw new NamespaceConflictException("Wrong namespace URI "
0895:                            + uri + " for the xml prefix");
0896:                }
0897:
0898:                this .URI = uri;
0899:
0900:            }
0901:
0902:            /**
0903:             * <p>
0904:             * Sets the namespace prefix of this element.
0905:             * You can pass null or the empty string to remove the prefix.
0906:             * </p>
0907:             * 
0908:             * @param prefix the new namespace prefix
0909:             * 
0910:             * @throws IllegalNameException if <code>prefix</code> is 
0911:             *     not a legal XML non-colonized name
0912:             * @throws NamespaceConflictException if <code>prefix</code> is 
0913:             *     already in use by an attribute or additional
0914:             *     namespace with a different URI than the element
0915:             *     itself
0916:             */
0917:            public void setNamespacePrefix(String prefix) {
0918:                _setNamespacePrefix(prefix);
0919:            }
0920:
0921:            private void _setNamespacePrefix(String prefix) {
0922:
0923:                if (prefix == null)
0924:                    prefix = "";
0925:
0926:                if (prefix.length() != 0)
0927:                    Verifier.checkNCName(prefix);
0928:
0929:                // Check how this affects or conflicts with
0930:                // attribute namespaces and additional
0931:                // namespace declarations.
0932:                String uri = getLocalNamespaceURI(prefix);
0933:                if (uri != null) {
0934:                    if (!uri.equals(this .URI) && !"xml".equals(prefix)) {
0935:                        throw new NamespaceConflictException(prefix
0936:                                + " conflicts with existing prefix");
0937:                    }
0938:                } else if ("".equals(this .URI) && !"".equals(prefix)) {
0939:                    throw new NamespaceConflictException(
0940:                            "Cannot assign prefix to element in no namespace");
0941:                }
0942:
0943:                this .prefix = prefix;
0944:
0945:            }
0946:
0947:            /**
0948:             * <p>
0949:             * Inserts a child node at the specified position.
0950:             * Inserting at position 0 makes the child the first child
0951:             * of this node. Inserting at the position 
0952:             * <code>getChildCount()</code>
0953:             * makes the child the last child of the node.
0954:             * </p>
0955:             * 
0956:             * <p>
0957:             * All the other methods that add a node to the tree,
0958:             * invoke this method ultimately.
0959:             * </p>
0960:             * 
0961:             * @param position where to insert the child
0962:             * @param child the node to insert
0963:             * 
0964:             * @throws IllegalAddException if <code>child</code> 
0965:             *   is a <code>Document</code>
0966:             * @throws MultipleParentException if <code>child</code> 
0967:             *   already has a parent
0968:             * @throws NullPointerException if <code>child</code> is null
0969:             * @throws IndexOutOfBoundsException if the position is negative 
0970:             *     or greater than the number of children of this element.
0971:             */
0972:            void insertionAllowed(Node child, int position) {
0973:
0974:                if (child == null) {
0975:                    throw new NullPointerException(
0976:                            "Tried to insert a null child in the tree");
0977:                } else if (child.getParent() != null) {
0978:                    throw new MultipleParentException(child.toString()
0979:                            + " child already has a parent.");
0980:                } else if (child.isElement()) {
0981:                    checkCycle(child, this );
0982:                    return;
0983:                } else if (child.isText() || child.isProcessingInstruction()
0984:                        || child.isComment()) {
0985:                    return;
0986:                } else {
0987:                    throw new IllegalAddException("Cannot add a "
0988:                            + child.getClass().getName() + " to an Element.");
0989:                }
0990:
0991:            }
0992:
0993:            private static void checkCycle(Node child, ParentNode parent) {
0994:
0995:                if (child == parent) {
0996:                    throw new CycleException("Cannot add a node to itself");
0997:                }
0998:                if (child.getChildCount() == 0)
0999:                    return;
1000:                while ((parent = parent.getParent()) != null) {
1001:                    if (parent == child) {
1002:                        throw new CycleException(
1003:                                "Cannot add an ancestor as a child");
1004:                    }
1005:                }
1006:
1007:            }
1008:
1009:            /**
1010:             * <p>
1011:             * Converts a string to a text node and inserts that
1012:             * node at the specified position.
1013:             * </p>
1014:             * 
1015:             * @param position where to insert the child
1016:             * @param text the string to convert to a text node and insert
1017:             * 
1018:             * @throws NullPointerException if text is null
1019:             * @throws IndexOutOfBoundsException if the position is negative
1020:             *     or greater than the number of children of the node
1021:             */
1022:            public void insertChild(String text, int position) {
1023:
1024:                if (text == null) {
1025:                    throw new NullPointerException("Inserted null string");
1026:                }
1027:                super .fastInsertChild(new Text(text), position);
1028:
1029:            }
1030:
1031:            /**
1032:             * <p>
1033:             * Converts a string to a text node
1034:             * and appends that node to the children of this node.
1035:             * </p>
1036:             * 
1037:             * @param text String to add to this node
1038:             * 
1039:             * @throws IllegalAddException if this node cannot 
1040:             *     have children of this type
1041:             * @throws NullPointerException if <code>text</code> is null
1042:             */
1043:            public void appendChild(String text) {
1044:                insertChild(new Text(text), getChildCount());
1045:            }
1046:
1047:            /**
1048:             * <p>
1049:             * Detaches all children from this node. 
1050:             * </p>
1051:             * 
1052:             * <p>
1053:             * Subclassers should note that the default implementation of this
1054:             * method does <strong>not</strong> call <code>removeChild</code>
1055:             * or <code>detach</code>. If you override 
1056:             * <code>removeChild</code>, you'll probably need to override this
1057:             * method as well.
1058:             * </p>
1059:             * 
1060:             * @return a list of all the children removed in the order they
1061:             *     appeared in the element
1062:             */
1063:            public Nodes removeChildren() {
1064:
1065:                int length = this .getChildCount();
1066:                Nodes result = new Nodes();
1067:                for (int i = 0; i < length; i++) {
1068:                    Node child = getChild(i);
1069:                    if (child.isElement())
1070:                        fillInBaseURI((Element) child);
1071:                    child.setParent(null);
1072:                    result.append(child);
1073:                }
1074:                this .children = null;
1075:                this .childCount = 0;
1076:
1077:                return result;
1078:
1079:            }
1080:
1081:            /**
1082:             * <p>
1083:             * Declares a namespace prefix. This is only necessary when
1084:             * prefixes are used in element content and attribute values,
1085:             * as in XSLT and the W3C XML Schema Language. Do not use 
1086:             * this method to declare prefixes for element and attribute 
1087:             * names.
1088:             * </p>
1089:             * 
1090:             * <p>
1091:             * If you do redeclare a prefix that is already used
1092:             * by an element or attribute name, the additional 
1093:             * namespace is added if and only if the URI is the same.
1094:             * Conflicting namespace declarations will throw an exception.
1095:             * </p>
1096:             * 
1097:             * @param prefix the prefix to declare
1098:             * @param uri the absolute URI reference to map the prefix to
1099:             * 
1100:             * @throws MalformedURIException if <code>URI</code> 
1101:             *      is not an RFC 3986 URI reference
1102:             * @throws IllegalNameException  if <code>prefix</code> is not 
1103:             *      a legal XML non-colonized name
1104:             * @throws NamespaceConflictException if the mapping conflicts 
1105:             *     with an existing element, attribute,
1106:             *     or additional namespace declaration
1107:             */
1108:            public void addNamespaceDeclaration(String prefix, String uri) {
1109:
1110:                if (prefix == null)
1111:                    prefix = "";
1112:                if (uri == null)
1113:                    uri = "";
1114:
1115:                // check to see if this is the xmlns or xml prefix
1116:                if (prefix.equals("xmlns")) {
1117:                    if (uri.equals("")) {
1118:                        // This is done automatically
1119:                        return;
1120:                    }
1121:                    throw new NamespaceConflictException(
1122:                            "The xmlns prefix cannot bound to any URI");
1123:                } else if (prefix.equals("xml")) {
1124:                    if (uri.equals("http://www.w3.org/XML/1998/namespace")) {
1125:                        // This is done automatically
1126:                        return;
1127:                    }
1128:                    throw new NamespaceConflictException(
1129:                            "Wrong namespace URI for xml prefix: " + uri);
1130:                } else if (uri.equals("http://www.w3.org/XML/1998/namespace")) {
1131:                    throw new NamespaceConflictException(
1132:                            "Wrong prefix for http://www.w3.org/XML/1998/namespace namespace: "
1133:                                    + prefix);
1134:                }
1135:
1136:                if (prefix.length() != 0) {
1137:                    Verifier.checkNCName(prefix);
1138:                    Verifier.checkAbsoluteURIReference(uri);
1139:                } else if (uri.length() != 0) {
1140:                    // Make sure we're not trying to undeclare 
1141:                    // the default namespace; this is legal.
1142:                    Verifier.checkAbsoluteURIReference(uri);
1143:                }
1144:
1145:                String currentBinding = getLocalNamespaceURI(prefix);
1146:                if (currentBinding != null && !currentBinding.equals(uri)) {
1147:
1148:                    String message;
1149:                    if (prefix.equals("")) {
1150:                        message = "Additional namespace " + uri
1151:                                + " conflicts with existing default namespace "
1152:                                + currentBinding;
1153:                    } else {
1154:                        message = "Additional namespace " + uri
1155:                                + " for the prefix " + prefix
1156:                                + " conflicts with existing namespace binding "
1157:                                + currentBinding;
1158:                    }
1159:                    throw new NamespaceConflictException(message);
1160:                }
1161:
1162:                if (namespaces == null)
1163:                    namespaces = new Namespaces();
1164:                namespaces.put(prefix, uri);
1165:
1166:            }
1167:
1168:            /**
1169:             * <p>
1170:             * Removes the mapping of the specified prefix. This method only
1171:             * removes additional namespaces added with 
1172:             * <code>addNamespaceDeclaration</code>.
1173:             * It has no effect on namespaces of elements and attributes.
1174:             * If the prefix is not used on this element, this method
1175:             * does nothing.
1176:             * </p>
1177:             * 
1178:             * @param prefix the prefix whose declaration should be removed
1179:             */
1180:            public void removeNamespaceDeclaration(String prefix) {
1181:
1182:                if (namespaces != null) {
1183:                    namespaces.remove(prefix);
1184:                }
1185:
1186:            }
1187:
1188:            /**
1189:             * <p>
1190:             * Returns the number of namespace declarations on this 
1191:             * element. This counts the namespace of the element 
1192:             * itself (which may be the empty string), the namespace  
1193:             * of each attribute, and each namespace added  
1194:             * by <code>addNamespaceDeclaration</code>.
1195:             * However, prefixes used multiple times are only counted 
1196:             * once; and the <code>xml</code> prefix used for 
1197:             * <code>xml:base</code>, <code>xml:lang</code>, and 
1198:             * <code>xml:space</code> is not counted even if one of these 
1199:             * attributes is present on the element.
1200:             * </p>
1201:             * 
1202:             * <p>
1203:             * The return value is almost always positive. It can be zero 
1204:             * if and only if the element itself has the prefix 
1205:             * <code>xml</code>; e.g. <code>&lt;xml:space /></code>.
1206:             * This is not endorsed by the XML specification. The prefix
1207:             * <code>xml</code> is reserved for use by the W3C, which has only
1208:             * used it for attributes to date. You really shouldn't do this.
1209:             * Nonetheless, this is not malformed so XOM allows it.
1210:             * </p>
1211:             * 
1212:             * @return the number of namespaces declared by this element
1213:             */
1214:            public final int getNamespaceDeclarationCount() {
1215:
1216:                // This seems to be a hot spot for DOM conversion.
1217:                // I'm trying to avoid the overhead of creating and adding
1218:                // to a HashSet for the simplest case of an element, none 
1219:                // of whose attributes are in namespaces, and which has no
1220:                // additional namespace declarations. In this case, the 
1221:                // namespace count is exactly one, which is here indicated
1222:                // by a null prefix set.
1223:                Set allPrefixes = null;
1224:                if (namespaces != null) {
1225:                    allPrefixes = new HashSet(namespaces.getPrefixes());
1226:                    allPrefixes.add(prefix);
1227:                }
1228:                if ("xml".equals(prefix))
1229:                    allPrefixes = new HashSet();
1230:                // add attribute prefixes
1231:                int count = getAttributeCount();
1232:                for (int i = 0; i < count; i++) {
1233:                    Attribute att = getAttribute(i);
1234:                    String attPrefix = att.getNamespacePrefix();
1235:                    if (attPrefix.length() != 0 && !"xml".equals(attPrefix)) {
1236:                        if (allPrefixes == null) {
1237:                            allPrefixes = new HashSet();
1238:                            allPrefixes.add(prefix);
1239:                        }
1240:                        allPrefixes.add(attPrefix);
1241:                    }
1242:                }
1243:
1244:                if (allPrefixes == null)
1245:                    return 1;
1246:                return allPrefixes.size();
1247:
1248:            }
1249:
1250:            // Used for XPath and serialization
1251:            Map getNamespacePrefixesInScope() {
1252:
1253:                HashMap namespaces = new HashMap();
1254:
1255:                Element current = this ;
1256:                while (current != null) {
1257:
1258:                    if (!("xml".equals(current.prefix))) {
1259:                        addPrefixIfNotAlreadyPresent(namespaces, current,
1260:                                current.prefix);
1261:                    }
1262:
1263:                    // add attribute prefixes
1264:                    int count = current.getAttributeCount();
1265:                    for (int i = 0; i < count; i++) {
1266:                        Attribute att = current.getAttribute(i);
1267:                        String attPrefix = att.getNamespacePrefix();
1268:                        if (attPrefix.length() != 0
1269:                                && !("xml".equals(attPrefix))) {
1270:                            addPrefixIfNotAlreadyPresent(namespaces, current,
1271:                                    attPrefix);
1272:                        }
1273:                    }
1274:
1275:                    // add additional namespace prefixes
1276:                    // ???? should add more methods to Namespaces to avoid access to private data
1277:                    if (current.namespaces != null) {
1278:                        int namespaceCount = current.namespaces.size();
1279:                        for (int i = 0; i < namespaceCount; i++) {
1280:                            String nsPrefix = current.namespaces.getPrefix(i);
1281:                            addPrefixIfNotAlreadyPresent(namespaces, current,
1282:                                    nsPrefix);
1283:                        }
1284:                    }
1285:
1286:                    ParentNode parent = current.getParent();
1287:                    if (parent == null || parent.isDocument()
1288:                            || parent.isDocumentFragment()) {
1289:                        break;
1290:                    }
1291:                    current = (Element) parent;
1292:                }
1293:
1294:                return namespaces;
1295:
1296:            }
1297:
1298:            private void addPrefixIfNotAlreadyPresent(HashMap namespaces,
1299:                    Element current, String prefix) {
1300:                if (!namespaces.containsKey(prefix)) {
1301:                    namespaces
1302:                            .put(prefix, current.getLocalNamespaceURI(prefix));
1303:                }
1304:            }
1305:
1306:            /**
1307:             * <p>
1308:             * Returns the index<sup>th</sup> namespace prefix <em>declared</em> on
1309:             * this element. Namespaces inherited from ancestors are not included.
1310:             * The index is purely for convenience, and has no
1311:             * meaning in itself. This includes the namespaces of the element
1312:             * name and of all attributes' names (except for those with the 
1313:             * prefix <code>xml</code> such as <code>xml:space</code>) as well 
1314:             * as additional declarations made for attribute values and element 
1315:             * content. However, prefixes used multiple times (e.g. on several
1316:             * attribute values) are only reported once. The default
1317:             * namespace is reported with an empty string prefix if present.
1318:             * Like most lists in Java, the first prefix is at index 0.
1319:             * </p>
1320:             * 
1321:             * <p>
1322:             * If the namespaces on the element change for any reason 
1323:             * (adding or removing an attribute in a namespace, adding 
1324:             * or removing a namespace declaration, changing the prefix 
1325:             * of an element, etc.) then then this method may skip or 
1326:             * repeat prefixes. Don't change the prefixes of an element  
1327:             * while iterating across them. 
1328:             * </p>
1329:             * 
1330:             * @param index the prefix to return
1331:             * 
1332:             * @return the prefix
1333:             * 
1334:             * @throws IndexOutOfBoundsException if <code>index</code> is 
1335:             *     negative or greater than or equal to the number of 
1336:             *     namespaces declared by this element.
1337:             * 
1338:             */
1339:            public final String getNamespacePrefix(int index) {
1340:
1341:                SortedSet allPrefixes;
1342:                if (namespaces != null) {
1343:                    allPrefixes = new TreeSet(namespaces.getPrefixes());
1344:                } else
1345:                    allPrefixes = new TreeSet();
1346:
1347:                if (!("xml".equals(prefix)))
1348:                    allPrefixes.add(prefix);
1349:
1350:                // add attribute prefixes
1351:                for (int i = 0; i < getAttributeCount(); i++) {
1352:                    Attribute att = getAttribute(i);
1353:                    String attPrefix = att.getNamespacePrefix();
1354:                    if (attPrefix.length() != 0 && !("xml".equals(attPrefix))) {
1355:                        allPrefixes.add(attPrefix);
1356:                    }
1357:                }
1358:
1359:                Iterator iterator = allPrefixes.iterator();
1360:                try {
1361:                    for (int i = 0; i < index; i++) {
1362:                        iterator.next();
1363:                    }
1364:                    return (String) iterator.next();
1365:                } catch (NoSuchElementException ex) {
1366:                    throw new IndexOutOfBoundsException("No " + index
1367:                            + "th namespace");
1368:                }
1369:
1370:            }
1371:
1372:            /**
1373:             * 
1374:             * <p>
1375:             * Sets the URI from which this element was loaded,
1376:             * and against which relative URLs in this element will be 
1377:             * resolved, unless an <code>xml:base</code> attribute overrides 
1378:             * this. Setting the base URI to null or the empty string removes 
1379:             * any existing base URI.
1380:             * </p>
1381:             * 
1382:             * @param URI the new base URI for this element
1383:             * 
1384:             * @throws MalformedURIException if <code>URI</code> is 
1385:             *     not a legal RFC 3986 absolute URI
1386:             */
1387:            public void setBaseURI(String URI) {
1388:                setActualBaseURI(URI);
1389:            }
1390:
1391:            /**
1392:             * 
1393:             * <p>
1394:             * Returns the absolute base URI against which relative URIs in 
1395:             * this element should be resolved. <code>xml:base</code> 
1396:             * attributes <em>in the same entity</em> take precedence over the
1397:             * actual base URI of the document where the element was found
1398:             * or which was set by <code>setBaseURI</code>.
1399:             * </p>
1400:             * 
1401:             * <p>
1402:             * This URI is made absolute before it is returned 
1403:             * by resolving the information in this element against the 
1404:             * information in its parent element and document entity.
1405:             * However, it is not always possible to fully absolutize the
1406:             * URI in all circumstances. In this case, this method returns the
1407:             * empty string to indicate the base URI of the current entity.
1408:             * </p>
1409:             * 
1410:             * <p>
1411:             * If the element's <code>xml:base</code> attribute contains a 
1412:             * value that is a syntactically illegal URI (e.g. %GF.html"),
1413:             * then the base URI is application dependent. XOM's choice is 
1414:             * to behave as if the element did not have an <code>xml:base</code>
1415:             * attribute. 
1416:             * </p>
1417:             * 
1418:             * @return the base URI of this element 
1419:             */
1420:            public String getBaseURI() {
1421:
1422:                String baseURI = "";
1423:                String sourceEntity = this .getActualBaseURI();
1424:
1425:                ParentNode current = this ;
1426:
1427:                while (true) {
1428:                    String currentEntity = current.getActualBaseURI();
1429:                    if (sourceEntity.length() != 0
1430:                            && !sourceEntity.equals(currentEntity)) {
1431:                        baseURI = URIUtil.absolutize(sourceEntity, baseURI);
1432:                        break;
1433:                    }
1434:
1435:                    if (current.isDocument()) {
1436:                        baseURI = URIUtil.absolutize(currentEntity, baseURI);
1437:                        break;
1438:                    }
1439:                    Attribute baseAttribute = ((Element) current).getAttribute(
1440:                            "base", "http://www.w3.org/XML/1998/namespace");
1441:                    if (baseAttribute != null) {
1442:                        String baseIRI = baseAttribute.getValue();
1443:                        // The base attribute contains an IRI, not a URI.
1444:                        // Thus the first thing we have to do is escape it
1445:                        // to convert illegal characters to hexadecimal escapes.
1446:                        String base = URIUtil.toURI(baseIRI);
1447:                        if ("".equals(base)) {
1448:                            baseURI = getEntityURI();
1449:                            break;
1450:                        } else if (legalURI(base)) {
1451:                            if ("".equals(baseURI))
1452:                                baseURI = base;
1453:                            else if (URIUtil.isOpaque(base))
1454:                                break;
1455:                            else
1456:                                baseURI = URIUtil.absolutize(base, baseURI);
1457:                            if (URIUtil.isAbsolute(base))
1458:                                break; // ???? base or baseURI
1459:                        }
1460:                    }
1461:                    current = current.getParent();
1462:                    if (current == null) {
1463:                        baseURI = URIUtil.absolutize(currentEntity, baseURI);
1464:                        break;
1465:                    }
1466:                }
1467:
1468:                if (URIUtil.isAbsolute(baseURI))
1469:                    return baseURI;
1470:                return "";
1471:
1472:            }
1473:
1474:            private String getEntityURI() {
1475:
1476:                ParentNode current = this ;
1477:                while (current != null) {
1478:                    if (current.actualBaseURI != null
1479:                            && current.actualBaseURI.length() != 0) {
1480:                        return current.actualBaseURI;
1481:                    }
1482:                    current = current.getParent();
1483:                }
1484:                return "";
1485:
1486:            }
1487:
1488:            private boolean legalURI(String base) {
1489:
1490:                try {
1491:                    Verifier.checkURIReference(base);
1492:                    return true;
1493:                } catch (MalformedURIException ex) {
1494:                    return false;
1495:                }
1496:
1497:            }
1498:
1499:            /**
1500:             * <p>
1501:             * Returns a string containing the XML serialization of this 
1502:             * element. This includes the element and all its attributes 
1503:             * and descendants. However, it does not contain namespace 
1504:             * declarations for namespaces inherited from ancestor elements.
1505:             * </p>
1506:             * 
1507:             * @return the XML representation of this element
1508:             * 
1509:             */
1510:            public final String toXML() {
1511:
1512:                StringBuffer result = new StringBuffer(1024);
1513:                Node current = this ;
1514:                boolean endTag = false;
1515:                int index = -1;
1516:                int[] indexes = new int[10];
1517:                int top = 0;
1518:                indexes[0] = -1;
1519:
1520:                while (true) {
1521:
1522:                    if (!endTag && current.getChildCount() > 0) {
1523:                        writeStartTag((Element) current, result);
1524:                        current = current.getChild(0);
1525:                        index = 0;
1526:                        top++;
1527:                        indexes = grow(indexes, top);
1528:                        indexes[top] = 0;
1529:                    } else {
1530:                        if (endTag) {
1531:                            writeEndTag((Element) current, result);
1532:                            if (current == this )
1533:                                break;
1534:                        } else if (current.isElement()) {
1535:                            writeStartTag((Element) current, result);
1536:                            if (current == this )
1537:                                break;
1538:                        } else {
1539:                            result.append(current.toXML());
1540:                        }
1541:                        endTag = false;
1542:                        ParentNode parent = current.getParent();
1543:                        if (parent.getChildCount() - 1 == index) {
1544:                            current = parent;
1545:                            top--;
1546:                            if (current != this ) {
1547:                                index = indexes[top];
1548:                            }
1549:                            endTag = true;
1550:                        } else {
1551:                            index++;
1552:                            indexes[top] = index;
1553:                            current = parent.getChild(index);
1554:                        }
1555:
1556:                    }
1557:
1558:                }
1559:
1560:                return result.toString();
1561:
1562:            }
1563:
1564:            private static void writeStartTag(Element element,
1565:                    StringBuffer result) {
1566:
1567:                result.append("<");
1568:                result.append(element.getQualifiedName());
1569:
1570:                ParentNode parentNode = element.getParent();
1571:                for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) {
1572:                    String additionalPrefix = element.getNamespacePrefix(i);
1573:                    String uri = element.getNamespaceURI(additionalPrefix);
1574:                    if (parentNode != null && parentNode.isElement()) {
1575:                        Element parentElement = (Element) parentNode;
1576:                        if (uri.equals(parentElement
1577:                                .getNamespaceURI(additionalPrefix))) {
1578:                            continue;
1579:                        }
1580:                    } else if (uri.length() == 0) {
1581:                        continue; // no need to say xmlns=""   
1582:                    }
1583:
1584:                    result.append(" xmlns");
1585:                    if (additionalPrefix.length() > 0) {
1586:                        result.append(':');
1587:                        result.append(additionalPrefix);
1588:                    }
1589:                    result.append("=\"");
1590:                    result.append(uri);
1591:                    result.append('"');
1592:                }
1593:
1594:                // attributes
1595:                if (element.attributes != null) {
1596:                    for (int i = 0; i < element.numAttributes; i++) {
1597:                        Attribute attribute = element.attributes[i];
1598:                        result.append(' ');
1599:                        result.append(attribute.toXML());
1600:                    }
1601:                }
1602:
1603:                if (element.getChildCount() > 0) {
1604:                    result.append('>');
1605:                } else {
1606:                    result.append(" />");
1607:                }
1608:
1609:            }
1610:
1611:            private static void writeEndTag(Element element, StringBuffer result) {
1612:
1613:                result.append("</");
1614:                result.append(element.getQualifiedName());
1615:                result.append(">");
1616:
1617:            }
1618:
1619:            /**
1620:             * <p>
1621:             * Returns the value of the element as defined by XPath 1.0.
1622:             * This is the complete PCDATA content of the element, without
1623:             * any tags, comments, or processing instructions after all 
1624:             * entity and character references have been resolved.
1625:             * </p>
1626:             * 
1627:             * @return XPath string value of this element
1628:             * 
1629:             */
1630:            public final String getValue() {
1631:
1632:                // non-recursive algorithm avoids stack size limitations
1633:                int childCount = this .getChildCount();
1634:                if (childCount == 0)
1635:                    return "";
1636:
1637:                Node current = this .getChild(0);
1638:                // optimization for common case where element 
1639:                // has a single text node child
1640:                if (childCount == 1 && current.isText()) {
1641:                    return current.getValue();
1642:                }
1643:
1644:                StringBuffer result = new StringBuffer();
1645:                int index = 0;
1646:                int[] indexes = new int[10];
1647:                int top = 0;
1648:                indexes[0] = 0;
1649:
1650:                boolean endTag = false;
1651:                while (true) {
1652:                    if (!endTag && current.getChildCount() > 0) {
1653:                        current = current.getChild(0);
1654:                        index = 0;
1655:                        top++;
1656:                        indexes = grow(indexes, top);
1657:                        indexes[top] = 0;
1658:                    } else {
1659:                        endTag = false;
1660:                        if (current.isText())
1661:                            result.append(current.getValue());
1662:                        ParentNode parent = current.getParent();
1663:                        if (parent.getChildCount() - 1 == index) {
1664:                            current = parent;
1665:                            top--;
1666:                            if (current == this )
1667:                                break;
1668:                            index = indexes[top];
1669:                            endTag = true;
1670:                        } else {
1671:                            index++;
1672:                            indexes[top] = index;
1673:                            current = parent.getChild(index);
1674:                        }
1675:                    }
1676:                }
1677:
1678:                return result.toString();
1679:
1680:            }
1681:
1682:            /**
1683:             * <p>
1684:             * Creates a deep copy of this element with no parent,
1685:             * that can be added to this document or a different one.
1686:             * </p>
1687:             * 
1688:             * <p>
1689:             * Subclassers should be wary. Implementing this method is trickier
1690:             * than it might seem, especially if you wish to avoid potential  
1691:             * stack overflows in deep documents. In particular, you should not
1692:             * rely on the obvious recursive algorithm. Most subclasses should
1693:             * override the {@link nu.xom.Element#shallowCopy() shallowCopy} 
1694:             * method instead.
1695:             * </p>
1696:             * 
1697:             * @return a deep copy of this element with no parent
1698:             */
1699:            public Node copy() {
1700:                Element result = copyTag(this );
1701:                copyChildren(this , result);
1702:                return result;
1703:            }
1704:
1705:            /**
1706:             * <p>
1707:             * Creates a very shallow copy of the element with the same name
1708:             * and namespace URI, but no children, attributes, base URI, or
1709:             * namespace declaration. This method is invoked as necessary
1710:             * by the {@link nu.xom.Element#copy() copy} method 
1711:             * and the {@link nu.xom.Element#Element(nu.xom.Element) 
1712:             * copy constructor}. 
1713:             * </p>
1714:             * 
1715:             * <p>
1716:             * Subclasses should override this method so that it
1717:             * returns an instance of the subclass so that types
1718:             * are preserved when copying. This method should not add any
1719:             * attributes, namespace declarations, or children to the 
1720:             * shallow copy. Any such items will be overwritten.
1721:             * </p>
1722:             *
1723:             * @return an empty element with the same name and 
1724:             *     namespace as this element
1725:             */
1726:            protected Element shallowCopy() {
1727:                return new Element(getQualifiedName(), getNamespaceURI());
1728:            }
1729:
1730:            /**
1731:             * <p>
1732:             * Returns a string representation of this element suitable
1733:             * for debugging and diagnosis. This is <em>not</em>
1734:             * the XML representation of the element.
1735:             * </p>
1736:             * 
1737:             * @return a non-XML string representation of this element
1738:             */
1739:            public final String toString() {
1740:                return "[" + getClass().getName() + ": " + getQualifiedName()
1741:                        + "]";
1742:            }
1743:
1744:            boolean isElement() {
1745:                return true;
1746:            }
1747:
1748:            private void checkPrefixConflict(Attribute attribute) {
1749:
1750:                String prefix = attribute.getNamespacePrefix();
1751:                String namespaceURI = attribute.getNamespaceURI();
1752:
1753:                // Look for conflicts
1754:                for (int i = 0; i < numAttributes; i++) {
1755:                    Attribute a = attributes[i];
1756:                    if (a.getNamespacePrefix().equals(prefix)) {
1757:                        if (a.getNamespaceURI().equals(namespaceURI))
1758:                            return;
1759:                        throw new NamespaceConflictException("Prefix of "
1760:                                + attribute.getQualifiedName()
1761:                                + " conflicts with " + a.getQualifiedName());
1762:                    }
1763:                }
1764:
1765:            }
1766:
1767:            Iterator attributeIterator() {
1768:
1769:                return new Iterator() {
1770:
1771:                    private int next = 0;
1772:
1773:                    public boolean hasNext() {
1774:                        return next < numAttributes;
1775:                    }
1776:
1777:                    public Object next() throws NoSuchElementException {
1778:
1779:                        if (hasNext()) {
1780:                            Attribute a = attributes[next];
1781:                            next++;
1782:                            return a;
1783:                        }
1784:                        throw new NoSuchElementException("No such attribute");
1785:
1786:                    }
1787:
1788:                    public void remove() {
1789:                        throw new UnsupportedOperationException();
1790:                    }
1791:
1792:                };
1793:            }
1794:
1795:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.