View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.html;
16  
17  import org.htmlunit.SgmlPage;
18  import org.w3c.dom.Attr;
19  import org.w3c.dom.TypeInfo;
20  
21  /**
22   * An attribute of an element. Attributes are stored in {@link HtmlElement},
23   * but the XPath engine expects attributes to be in a {@link DomNode}.
24   *
25   * @author Denis N. Antonioli
26   * @author David K. Taylor
27   * @author Ahmed Ashour
28   * @author Ronald Brill
29   */
30  public class DomAttr extends DomNamespaceNode implements Attr {
31  
32      private String value_;
33      private boolean specified_;
34  
35      /**
36       * Instantiate a new attribute.
37       *
38       * @param page the page that the attribute belongs to
39       * @param namespaceURI the namespace that defines the attribute name (maybe {@code null})
40       * @param qualifiedName the name of the attribute
41       * @param value the value of the attribute
42       * @param specified {@code true} if this attribute was explicitly given a value in the source document,
43       *        or if the application changed the value of the attribute
44       */
45      public DomAttr(final SgmlPage page, final String namespaceURI, final String qualifiedName, final String value,
46          final boolean specified) {
47          super(namespaceURI, qualifiedName, page);
48          value_ = normalizeValue(value);
49          specified_ = specified;
50      }
51  
52      private static String normalizeValue(final String value) {
53          if (value != null && value.isEmpty()) {
54              return DomElement.ATTRIBUTE_VALUE_EMPTY;
55          }
56          return value;
57      }
58  
59      /**
60       * {@inheritDoc}
61       */
62      @Override
63      public short getNodeType() {
64          return ATTRIBUTE_NODE;
65      }
66  
67      /**
68       * {@inheritDoc}
69       */
70      @Override
71      public String getNodeName() {
72          return getName();
73      }
74  
75      /**
76       * {@inheritDoc}
77       */
78      @Override
79      public String getNodeValue() {
80          return getValue();
81      }
82  
83      /**
84       * {@inheritDoc}
85       */
86      @Override
87      public String getName() {
88          return getQualifiedName();
89      }
90  
91      /**
92       * {@inheritDoc}
93       */
94      @Override
95      public String getValue() {
96          return value_;
97      }
98  
99      /**
100      * {@inheritDoc}
101      */
102     @Override
103     public void setNodeValue(final String value) {
104         setValue(value);
105     }
106 
107     /**
108      * {@inheritDoc}
109      */
110     @Override
111     public void setValue(final String value) {
112         value_ = normalizeValue(value);
113         specified_ = true;
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public DomElement getOwnerElement() {
121         return (DomElement) getParentNode();
122     }
123 
124     /**
125      * {@inheritDoc}
126      */
127     @Override
128     public boolean getSpecified() {
129         return specified_;
130     }
131 
132     /**
133      * {@inheritDoc}
134      * Not yet implemented.
135      */
136     @Override
137     public TypeInfo getSchemaTypeInfo() {
138         throw new UnsupportedOperationException("DomAttr.getSchemaTypeInfo is not yet implemented.");
139     }
140 
141     /**
142      * {@inheritDoc}
143      */
144     @Override
145     public boolean isId() {
146         return DomElement.ID_ATTRIBUTE.equals(getNodeName());
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     @Override
153     public String toString() {
154         return getClass().getSimpleName() + "[name=" + getNodeName() + " value=" + getNodeValue() + "]";
155     }
156 
157     /**
158      * {@inheritDoc}
159      */
160     @Override
161     public String getCanonicalXPath() {
162         final DomNode parent = getParentNode();
163         if (parent == null) {
164             return "/@" + getName();
165         }
166         return parent.getCanonicalXPath() + "/@" + getName();
167     }
168 
169     /**
170      * {@inheritDoc}
171      */
172     @Override
173     public String getTextContent() {
174         return getNodeValue();
175     }
176 
177     /**
178      * {@inheritDoc}
179      */
180     @Override
181     public void setTextContent(final String textContent) {
182         final DomElement owner = getOwnerElement();
183         final boolean mappedElement = owner != null
184                 && getOwnerDocument() instanceof HtmlPage
185                 && (DomElement.NAME_ATTRIBUTE.equals(getName()) || DomElement.ID_ATTRIBUTE.equals(getName()));
186 
187         final HtmlPage htmlPage = mappedElement ? (HtmlPage) getPage() : null;
188 
189         if (mappedElement) {
190             htmlPage.removeMappedElement(owner, false, false);
191         }
192         setValue(textContent);
193         if (mappedElement) {
194             htmlPage.addMappedElement(owner, false);
195         }
196     }
197 }