internalize rexml
[booh] / lib / booh / rexml / attribute.rb
1 require "booh/rexml/namespace"
2 require 'booh/rexml/text'
3
4 module REXML
5         # Defines an Element Attribute; IE, a attribute=value pair, as in:
6         # <element attribute="value"/>.  Attributes can be in their own
7         # namespaces.  General users of REXML will not interact with the
8         # Attribute class much.
9         class Attribute
10                 include Node
11                 include Namespace
12
13                 # The element to which this attribute belongs
14                 attr_reader :element
15                 # The normalized value of this attribute.  That is, the attribute with
16                 # entities intact.
17                 attr_writer :normalized 
18                 PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um
19
20                 # Constructor.
21     # FIXME: The parser doesn't catch illegal characters in attributes
22     #
23     # first:: 
24     #   Either: an Attribute, which this new attribute will become a
25     #   clone of; or a String, which is the name of this attribute
26     # second::
27     #   If +first+ is an Attribute, then this may be an Element, or nil.
28     #   If nil, then the Element parent of this attribute is the parent
29     #   of the +first+ Attribute.  If the first argument is a String, 
30     #   then this must also be a String, and is the content of the attribute.  
31     #   If this is the content, it must be fully normalized (contain no
32     #   illegal characters).
33     # parent::
34     #   Ignored unless +first+ is a String; otherwise, may be the Element 
35     #   parent of this attribute, or nil.
36     #
37                 #
38                 #  Attribute.new( attribute_to_clone )
39                 #  Attribute.new( attribute_to_clone, parent_element )
40                 #  Attribute.new( "attr", "attr_value" )
41                 #  Attribute.new( "attr", "attr_value", parent_element )
42                 def initialize( first, second=nil, parent=nil )
43                         @normalized = @unnormalized = @element = nil
44                         if first.kind_of? Attribute
45                                 self.name = first.expanded_name
46                                 @unnormalized = first.value
47                                 if second.kind_of? Element
48                                         @element = second
49                                 else
50                                         @element = first.element
51                                 end
52                         elsif first.kind_of? String
53                                 @element = parent if parent.kind_of? Element
54                                 self.name = first
55                                 @normalized = second.to_s
56                         else
57                                 raise "illegal argument #{first.class.name} to Attribute constructor"
58                         end
59                 end
60
61                 # Returns the namespace of the attribute.
62                 # 
63                 #  e = Element.new( "elns:myelement" )
64                 #  e.add_attribute( "nsa:a", "aval" )
65                 #  e.add_attribute( "b", "bval" )
66                 #  e.attributes.get_attribute( "a" ).prefix   # -> "nsa"
67                 #  e.attributes.get_attribute( "b" ).prefix   # -> "elns"
68                 #  a = Attribute.new( "x", "y" )
69                 #  a.prefix                                   # -> ""
70                 def prefix
71                         pf = super
72                         if pf == ""
73                                 pf = @element.prefix if @element
74                         end
75                         pf
76                 end
77
78                 # Returns the namespace URL, if defined, or nil otherwise
79                 # 
80                 #  e = Element.new("el")
81                 #  e.add_attributes({"xmlns:ns", "http://url"})
82                 #  e.namespace( "ns" )              # -> "http://url"
83                 def namespace arg=nil
84                         arg = prefix if arg.nil?
85                         @element.namespace arg
86                 end
87
88                 # Returns true if other is an Attribute and has the same name and value,
89                 # false otherwise.
90                 def ==( other )
91                         other.kind_of?(Attribute) and other.name==name and other.value==value
92                 end
93
94                 # Creates (and returns) a hash from both the name and value
95                 def hash
96                         name.hash + value.hash
97                 end
98
99                 # Returns this attribute out as XML source, expanding the name
100                 #
101                 #  a = Attribute.new( "x", "y" )
102                 #  a.to_string     # -> "x='y'"
103                 #  b = Attribute.new( "ns:x", "y" )
104                 #  b.to_string     # -> "ns:x='y'"
105                 def to_string
106                         "#@expanded_name='#{to_s().gsub(/'/, '&apos;')}'"
107                 end
108
109                 # Returns the attribute value, with entities replaced
110                 def to_s
111                         return @normalized if @normalized
112
113                         doctype = nil
114                         if @element
115                                 doc = @element.document
116                                 doctype = doc.doctype if doc
117                         end
118
119                         @normalized = Text::normalize( @unnormalized, doctype )
120                         @unnormalized = nil
121       @normalized
122                 end
123
124                 # Returns the UNNORMALIZED value of this attribute.  That is, entities
125                 # have been expanded to their values
126                 def value
127                         return @unnormalized if @unnormalized
128                         doctype = nil
129                         if @element
130                                 doc = @element.document
131                                 doctype = doc.doctype if doc
132                         end
133                         @unnormalized = Text::unnormalize( @normalized, doctype )
134                         @normalized = nil
135       @unnormalized
136                 end
137
138                 # Returns a copy of this attribute
139                 def clone
140                         Attribute.new self
141                 end
142
143                 # Sets the element of which this object is an attribute.  Normally, this
144                 # is not directly called.
145                 #
146                 # Returns this attribute
147                 def element=( element )
148                         @element = element
149                         self
150                 end
151
152                 # Removes this Attribute from the tree, and returns true if successfull
153                 # 
154                 # This method is usually not called directly.
155                 def remove
156                         @element.attributes.delete self.name unless @element.nil?
157                 end
158
159                 # Writes this attribute (EG, puts 'key="value"' to the output)
160                 def write( output, indent=-1 )
161                         output << to_string
162                 end
163
164     def node_type
165       :attribute
166     end
167
168     def inspect
169       rv = ""
170       write( rv )
171       rv
172     end
173
174     def xpath
175       path = @element.xpath
176       path += "/@#{self.expanded_name}"
177       return path
178     end
179         end
180 end
181 #vim:ts=2 sw=2 noexpandtab: