nokogiri 1.5.0.beta.1 → 1.5.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (114) hide show
  1. data/CHANGELOG.ja.rdoc +28 -8
  2. data/CHANGELOG.rdoc +23 -0
  3. data/Manifest.txt +63 -1
  4. data/README.ja.rdoc +1 -1
  5. data/README.rdoc +22 -4
  6. data/Rakefile +6 -2
  7. data/ext/java/nokogiri/EncodingHandler.java +92 -0
  8. data/ext/java/nokogiri/HtmlDocument.java +116 -0
  9. data/ext/java/nokogiri/HtmlElementDescription.java +111 -0
  10. data/ext/java/nokogiri/HtmlEntityLookup.java +45 -0
  11. data/ext/java/nokogiri/HtmlSaxParserContext.java +218 -0
  12. data/ext/java/nokogiri/NokogiriService.java +370 -0
  13. data/ext/java/nokogiri/XmlAttr.java +147 -0
  14. data/ext/java/nokogiri/XmlAttributeDecl.java +98 -0
  15. data/ext/java/nokogiri/XmlCdata.java +50 -0
  16. data/ext/java/nokogiri/XmlComment.java +47 -0
  17. data/ext/java/nokogiri/XmlDocument.java +463 -0
  18. data/ext/java/nokogiri/XmlDocumentFragment.java +207 -0
  19. data/ext/java/nokogiri/XmlDtd.java +427 -0
  20. data/ext/java/nokogiri/XmlElement.java +172 -0
  21. data/ext/java/nokogiri/XmlElementContent.java +350 -0
  22. data/ext/java/nokogiri/XmlElementDecl.java +115 -0
  23. data/ext/java/nokogiri/XmlEntityDecl.java +129 -0
  24. data/ext/java/nokogiri/XmlEntityReference.java +42 -0
  25. data/ext/java/nokogiri/XmlNamespace.java +77 -0
  26. data/ext/java/nokogiri/XmlNode.java +1399 -0
  27. data/ext/java/nokogiri/XmlNodeSet.java +248 -0
  28. data/ext/java/nokogiri/XmlProcessingInstruction.java +70 -0
  29. data/ext/java/nokogiri/XmlReader.java +373 -0
  30. data/ext/java/nokogiri/XmlRelaxng.java +166 -0
  31. data/ext/java/nokogiri/XmlSaxParserContext.java +308 -0
  32. data/ext/java/nokogiri/XmlSaxPushParser.java +146 -0
  33. data/ext/java/nokogiri/XmlSchema.java +142 -0
  34. data/ext/java/nokogiri/XmlSyntaxError.java +84 -0
  35. data/ext/java/nokogiri/XmlText.java +96 -0
  36. data/ext/java/nokogiri/XmlXpathContext.java +130 -0
  37. data/ext/java/nokogiri/XsltStylesheet.java +126 -0
  38. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +181 -0
  39. data/ext/java/nokogiri/internals/NokogiriDocumentCache.java +39 -0
  40. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +42 -0
  41. data/ext/java/nokogiri/internals/NokogiriHandler.java +251 -0
  42. data/ext/java/nokogiri/internals/NokogiriHelpers.java +526 -0
  43. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +136 -0
  44. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +80 -0
  45. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +37 -0
  46. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +54 -0
  47. data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +49 -0
  48. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +88 -0
  49. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +23 -0
  50. data/ext/java/nokogiri/internals/ParserContext.java +235 -0
  51. data/ext/java/nokogiri/internals/PushInputStream.java +381 -0
  52. data/ext/java/nokogiri/internals/ReaderNode.java +431 -0
  53. data/ext/java/nokogiri/internals/SaveContext.java +249 -0
  54. data/ext/java/nokogiri/internals/SchemaErrorHandler.java +35 -0
  55. data/ext/java/nokogiri/internals/XmlDeclHandler.java +10 -0
  56. data/ext/java/nokogiri/internals/XmlDomParser.java +45 -0
  57. data/ext/java/nokogiri/internals/XmlDomParserContext.java +201 -0
  58. data/ext/java/nokogiri/internals/XmlSaxParser.java +33 -0
  59. data/ext/nokogiri/depend +32 -0
  60. data/ext/nokogiri/extconf.rb +61 -32
  61. data/ext/nokogiri/nokogiri.c +0 -5
  62. data/ext/nokogiri/nokogiri.h +2 -2
  63. data/ext/nokogiri/xml_document.c +5 -0
  64. data/ext/nokogiri/xml_libxml2_hacks.c +112 -0
  65. data/ext/nokogiri/xml_libxml2_hacks.h +12 -0
  66. data/ext/nokogiri/xml_node.c +56 -16
  67. data/ext/nokogiri/xml_node_set.c +7 -7
  68. data/ext/nokogiri/xml_reader.c +20 -1
  69. data/ext/nokogiri/xml_relax_ng.c +0 -7
  70. data/ext/nokogiri/xml_xpath_context.c +2 -0
  71. data/lib/isorelax.jar +0 -0
  72. data/lib/jing.jar +0 -0
  73. data/lib/nekodtd.jar +0 -0
  74. data/lib/nekohtml.jar +0 -0
  75. data/lib/nokogiri.rb +1 -2
  76. data/lib/nokogiri/css/generated_parser.rb +155 -148
  77. data/lib/nokogiri/css/generated_tokenizer.rb +2 -1
  78. data/lib/nokogiri/css/parser.y +3 -0
  79. data/lib/nokogiri/css/xpath_visitor.rb +1 -7
  80. data/lib/nokogiri/html.rb +2 -2
  81. data/lib/nokogiri/html/document_fragment.rb +7 -4
  82. data/lib/nokogiri/nokogiri.jar +0 -0
  83. data/lib/nokogiri/version.rb +3 -6
  84. data/lib/nokogiri/xml/builder.rb +1 -1
  85. data/lib/nokogiri/xml/document.rb +1 -2
  86. data/lib/nokogiri/xml/document_fragment.rb +7 -0
  87. data/lib/nokogiri/xml/node.rb +5 -3
  88. data/lib/nokogiri/xml/node_set.rb +25 -0
  89. data/lib/nokogiri/xml/reader.rb +2 -0
  90. data/lib/nokogiri/xml/sax/document.rb +3 -1
  91. data/lib/xercesImpl.jar +0 -0
  92. data/spec/helper.rb +3 -0
  93. data/spec/xml/reader_spec.rb +307 -0
  94. data/tasks/test.rb +1 -1
  95. data/test/css/test_parser.rb +11 -1
  96. data/test/html/sax/test_parser_context.rb +2 -2
  97. data/test/html/test_document.rb +2 -2
  98. data/test/html/test_document_fragment.rb +34 -6
  99. data/test/test_memory_leak.rb +2 -2
  100. data/test/test_reader.rb +28 -6
  101. data/test/test_xslt_transforms.rb +2 -3
  102. data/test/xml/test_attr.rb +31 -4
  103. data/test/xml/test_builder.rb +5 -5
  104. data/test/xml/test_cdata.rb +3 -3
  105. data/test/xml/test_document.rb +8 -8
  106. data/test/xml/test_document_fragment.rb +4 -12
  107. data/test/xml/test_node.rb +1 -1
  108. data/test/xml/test_node_reparenting.rb +26 -11
  109. data/test/xml/test_node_set.rb +38 -2
  110. data/test/xml/test_text.rb +11 -2
  111. data/test/xml/test_unparented_node.rb +1 -1
  112. data/test/xml/test_xpath.rb +11 -7
  113. metadata +68 -5
  114. data/lib/nokogiri/version_warning.rb +0 -14
@@ -0,0 +1,42 @@
1
+ package nokogiri.internals;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.List;
5
+
6
+ import nokogiri.XmlSyntaxError;
7
+
8
+ import org.apache.xerces.xni.parser.XMLErrorHandler;
9
+ import org.jruby.Ruby;
10
+ import org.jruby.runtime.ThreadContext;
11
+ import org.jruby.runtime.builtin.IRubyObject;
12
+ import org.xml.sax.ErrorHandler;
13
+
14
+ /**
15
+ *
16
+ * @author sergio
17
+ */
18
+ public abstract class NokogiriErrorHandler
19
+ implements ErrorHandler, XMLErrorHandler {
20
+
21
+ protected List<Exception> errors;
22
+
23
+ public NokogiriErrorHandler() {
24
+ this.errors = new ArrayList<Exception>();
25
+ }
26
+
27
+ void addError(Exception e) {
28
+ errors.add(e);
29
+ }
30
+
31
+ public List<Exception> getErrors() { return this.errors; }
32
+
33
+ public List<IRubyObject> getErrorsReadyForRuby(ThreadContext context){
34
+ Ruby ruby = context.getRuntime();
35
+ List<IRubyObject> res = new ArrayList<IRubyObject>();
36
+ for(int i = 0; i < this.errors.size(); i++) {
37
+ res.add(new XmlSyntaxError(ruby, this.errors.get(i)));
38
+ }
39
+ return res;
40
+ }
41
+
42
+ }
@@ -0,0 +1,251 @@
1
+ package nokogiri.internals;
2
+
3
+ import java.util.Arrays;
4
+ import java.util.LinkedList;
5
+ import static nokogiri.internals.NokogiriHelpers.getLocalPart;
6
+ import static nokogiri.internals.NokogiriHelpers.getPrefix;
7
+ import static nokogiri.internals.NokogiriHelpers.isNamespace;
8
+ import static nokogiri.internals.NokogiriHelpers.stringOrNil;
9
+
10
+ import java.util.logging.Logger;
11
+
12
+ import nokogiri.XmlSyntaxError;
13
+ import org.jruby.Ruby;
14
+ import org.jruby.RubyArray;
15
+ import org.jruby.RubyClass;
16
+ import org.jruby.RubyObject;
17
+ import org.jruby.javasupport.util.RuntimeHelpers;
18
+ import org.jruby.runtime.ThreadContext;
19
+ import org.jruby.runtime.builtin.IRubyObject;
20
+ import org.xml.sax.Attributes;
21
+ import org.xml.sax.SAXException;
22
+ import org.xml.sax.SAXParseException;
23
+ import org.xml.sax.ext.DefaultHandler2;
24
+
25
+ /**
26
+ *
27
+ * @author sergio
28
+ */
29
+ public class NokogiriHandler extends DefaultHandler2
30
+ implements XmlDeclHandler {
31
+
32
+ private static Logger LOGGER = Logger.getLogger(NokogiriHandler.class.getName());
33
+
34
+ boolean inCDATA = false;
35
+
36
+ private Ruby ruby;
37
+ private RubyClass attrClass;
38
+ private IRubyObject object;
39
+ private boolean namespaceDefined = false;
40
+
41
+ /**
42
+ * Stores parse errors with the most-recent error last.
43
+ *
44
+ * TODO: should these be stored in the document 'errors' array?
45
+ * Currently only string messages are stored there.
46
+ */
47
+ private LinkedList<XmlSyntaxError> errors =
48
+ new LinkedList<XmlSyntaxError>();
49
+
50
+ public NokogiriHandler(Ruby ruby, IRubyObject object) {
51
+ this.ruby = ruby;
52
+ this.attrClass = (RubyClass) ruby.getClassFromPath(
53
+ "Nokogiri::XML::SAX::Parser::Attribute");
54
+ this.object = object;
55
+ }
56
+
57
+ @Override
58
+ public void startDocument() throws SAXException {
59
+ call("start_document");
60
+ }
61
+
62
+ public void xmlDecl(String version, String encoding, String standalone) {
63
+ call("xmldecl", stringOrNil(ruby, version),
64
+ stringOrNil(ruby, encoding),
65
+ stringOrNil(ruby, standalone));
66
+ }
67
+
68
+ @Override
69
+ public void endDocument() throws SAXException {
70
+ call("end_document");
71
+ }
72
+
73
+ /*
74
+ * This has to call either "start_element" or
75
+ * "start_element_namespace" depending on whether there are any
76
+ * namespace attributes.
77
+ *
78
+ * Attributes that define namespaces are passed in a separate
79
+ * array of of <code>[:prefix, :uri]</code> arrays and are not
80
+ * passed with the other attributes.
81
+ */
82
+ @Override
83
+ public void startElement(String uri, String localName, String qName,
84
+ Attributes attrs) throws SAXException {
85
+ // for attributes other than namespace attrs
86
+ RubyArray rubyAttr = RubyArray.newArray(ruby);
87
+ // for namespace defining attributes
88
+ RubyArray rubyNSAttr = RubyArray.newArray(ruby);
89
+
90
+ ThreadContext context = ruby.getCurrentContext();
91
+ boolean fromFragmentHandler = false; // isFromFragmentHandler();
92
+
93
+ for (int i = 0; i < attrs.getLength(); i++) {
94
+ String u = attrs.getURI(i);
95
+ String qn = attrs.getQName(i);
96
+ String ln = attrs.getLocalName(i);
97
+ String val = attrs.getValue(i);
98
+ String pre;
99
+
100
+ pre = getPrefix(qn);
101
+ if (ln == null || ln.equals("")) ln = getLocalPart(qn);
102
+
103
+ if (isNamespace(qn) && !fromFragmentHandler) {
104
+ // I haven't figured the reason out yet, but, in somewhere,
105
+ // namespace is converted to array in array in array and cause
106
+ // TypeError at line 45 in fragment_handler.rb
107
+ RubyArray ns = RubyArray.newArray(ruby, 2);
108
+ if (ln.equals("xmlns")) ln = null;
109
+ ns.add(stringOrNil(ruby, ln));
110
+ ns.add(ruby.newString(val));
111
+ rubyNSAttr.add(ns);
112
+ } else {
113
+ IRubyObject[] args = new IRubyObject[4];
114
+ args[0] = stringOrNil(ruby, ln);
115
+ args[1] = stringOrNil(ruby, pre);
116
+ args[2] = stringOrNil(ruby, u);
117
+ args[3] = stringOrNil(ruby, val);
118
+
119
+ IRubyObject attr =
120
+ RuntimeHelpers.invoke(context, attrClass, "new", args);
121
+ rubyAttr.add(attr);
122
+ }
123
+ }
124
+
125
+ if (localName == null || localName.equals(""))
126
+ localName = getLocalPart(qName);
127
+ call("start_element_namespace",
128
+ stringOrNil(ruby, localName),
129
+ rubyAttr,
130
+ stringOrNil(ruby, getPrefix(qName)),
131
+ stringOrNil(ruby, uri),
132
+ rubyNSAttr);
133
+ }
134
+
135
+ private boolean isFromFragmentHandler() {
136
+ if (object != null && object instanceof RubyObject) {
137
+ RubyObject rubyObj = (RubyObject)object;
138
+ IRubyObject document = rubyObj.getInstanceVariable("@document");
139
+ if (document != null) {
140
+ String name = document.getMetaClass().getName();
141
+ if ("Nokogiri::XML::FragmentHandler".equals(name)) {
142
+ return true;
143
+ }
144
+ }
145
+ }
146
+ return false;
147
+ }
148
+
149
+ @Override
150
+ public void endElement(String uri, String localName, String qName) throws SAXException {
151
+ call("end_element_namespace",
152
+ stringOrNil(ruby, localName),
153
+ stringOrNil(ruby, getPrefix(qName)),
154
+ stringOrNil(ruby, uri));
155
+ }
156
+
157
+ @Override
158
+ public void characters(char[] ch, int start, int length) throws SAXException {
159
+ String target = inCDATA ? "cdata_block" : "characters";
160
+ call(target, ruby.newString(new String(ch, start, length)));
161
+ }
162
+
163
+ @Override
164
+ public void comment(char[] ch, int start, int length) throws SAXException {
165
+ call("comment", ruby.newString(new String(ch, start, length)));
166
+ }
167
+
168
+ @Override
169
+ public void startCDATA() throws SAXException {
170
+ inCDATA = true;
171
+ }
172
+
173
+ @Override
174
+ public void endCDATA() throws SAXException {
175
+ inCDATA = false;
176
+ }
177
+
178
+ @Override
179
+ public void error(SAXParseException saxpe) {
180
+ addError(XmlSyntaxError.createError(ruby, saxpe));
181
+ call("error", ruby.newString(saxpe.getMessage()));
182
+ }
183
+
184
+ @Override
185
+ public void fatalError(SAXParseException saxpe) throws SAXException
186
+ {
187
+ addError(XmlSyntaxError.createFatalError(ruby, saxpe));
188
+ call("error", ruby.newString(saxpe.getMessage()));
189
+ }
190
+
191
+ @Override
192
+ public void warning(SAXParseException saxpe) {
193
+ //System.out.println("warning: " + saxpe);
194
+ call("warning", ruby.newString(saxpe.getMessage()));
195
+ }
196
+
197
+ protected synchronized void addError(XmlSyntaxError e) {
198
+ errors.add(e);
199
+ }
200
+
201
+ public synchronized int getErrorCount() {
202
+ return errors.size();
203
+ }
204
+
205
+ public synchronized IRubyObject getLastError() {
206
+ return errors.getLast();
207
+ }
208
+
209
+ private void call(String methodName) {
210
+ ThreadContext context = ruby.getCurrentContext();
211
+ RuntimeHelpers.invoke(context, document(context), methodName);
212
+ }
213
+
214
+ private void call(String methodName, IRubyObject argument) {
215
+ ThreadContext context = ruby.getCurrentContext();
216
+ RuntimeHelpers.invoke(context, document(context), methodName, argument);
217
+ }
218
+
219
+ private void call(String methodName, IRubyObject arg1, IRubyObject arg2) {
220
+ ThreadContext context = ruby.getCurrentContext();
221
+ RuntimeHelpers.invoke(context, document(context), methodName, arg1, arg2);
222
+ }
223
+
224
+ private void call(String methodName, IRubyObject arg1, IRubyObject arg2,
225
+ IRubyObject arg3) {
226
+ ThreadContext context = ruby.getCurrentContext();
227
+ RuntimeHelpers.invoke(context, document(context), methodName,
228
+ arg1, arg2, arg3);
229
+ }
230
+
231
+ private void call(String methodName,
232
+ IRubyObject arg0,
233
+ IRubyObject arg1,
234
+ IRubyObject arg2,
235
+ IRubyObject arg3,
236
+ IRubyObject arg4) {
237
+ IRubyObject[] args = new IRubyObject[5];
238
+ args[0] = arg0;
239
+ args[1] = arg1;
240
+ args[2] = arg2;
241
+ args[3] = arg3;
242
+ args[4] = arg4;
243
+ ThreadContext context = ruby.getCurrentContext();
244
+ RuntimeHelpers.invoke(context, document(context), methodName, args);
245
+ }
246
+
247
+ private IRubyObject document(ThreadContext context){
248
+ return RuntimeHelpers.invoke(context, this.object, "document");
249
+ }
250
+
251
+ }
@@ -0,0 +1,526 @@
1
+ package nokogiri.internals;
2
+
3
+ import java.nio.ByteBuffer;
4
+ import java.nio.charset.Charset;
5
+
6
+ import nokogiri.NokogiriService;
7
+ import nokogiri.XmlAttr;
8
+ import nokogiri.XmlCdata;
9
+ import nokogiri.XmlComment;
10
+ import nokogiri.XmlDocument;
11
+ import nokogiri.XmlElement;
12
+ import nokogiri.XmlNamespace;
13
+ import nokogiri.XmlNode;
14
+ import nokogiri.XmlText;
15
+
16
+ import org.jruby.Ruby;
17
+ import org.jruby.RubyArray;
18
+ import org.jruby.RubyClass;
19
+ import org.jruby.RubyEncoding;
20
+ import org.jruby.RubyHash;
21
+ import org.jruby.RubyString;
22
+ import org.jruby.javasupport.JavaUtil;
23
+ import org.jruby.runtime.builtin.IRubyObject;
24
+ import org.jruby.util.ByteList;
25
+ import org.w3c.dom.Attr;
26
+ import org.w3c.dom.Document;
27
+ import org.w3c.dom.NamedNodeMap;
28
+ import org.w3c.dom.Node;
29
+ import org.w3c.dom.NodeList;
30
+
31
+ /**
32
+ *
33
+ * @author serabe
34
+ */
35
+ public class NokogiriHelpers {
36
+ public static final String CACHED_NODE = "NOKOGIRI_CACHED_NODE";
37
+ public static final String VALID_ROOT_NODE = "NOKOGIRI_VALIDE_ROOT_NODE";
38
+
39
+ public static XmlNode getCachedNode(Node node) {
40
+ return (XmlNode) node.getUserData(CACHED_NODE);
41
+ }
42
+
43
+ /**
44
+ * Get the XmlNode associated with the underlying
45
+ * <code>node</code>. Creates a new XmlNode (or appropriate subclass)
46
+ * or XmlNamespace wrapping <code>node</code> if there is no cached
47
+ * value.
48
+ */
49
+ public static IRubyObject getCachedNodeOrCreate(Ruby ruby, Node node) {
50
+ if(node == null) return ruby.getNil();
51
+ if (node.getNodeType() == Node.ATTRIBUTE_NODE && isNamespace(node.getNodeName())) {
52
+ XmlDocument xmlDocument = (XmlDocument)node.getOwnerDocument().getUserData(CACHED_NODE);
53
+ XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(node.getNodeName(), node.getNodeName());
54
+ if (xmlNamespace == null) {
55
+ String prefix = getLocalNameForNamespace(((Attr)node).getName());
56
+ prefix = prefix != null ? prefix : "";
57
+ return xmlDocument.getNamespaceCache().put(ruby, prefix, ((Attr)node).getValue(), node, xmlDocument);
58
+ }
59
+ }
60
+ XmlNode xmlNode = getCachedNode(node);
61
+ if(xmlNode == null) {
62
+ xmlNode = (XmlNode)constructNode(ruby, node);
63
+ node.setUserData(CACHED_NODE, xmlNode, null);
64
+ }
65
+ return xmlNode;
66
+ }
67
+
68
+ /**
69
+ * Construct a new XmlNode wrapping <code>node</code>. The proper
70
+ * subclass of XmlNode is chosen based on the type of
71
+ * <code>node</code>.
72
+ */
73
+ public static IRubyObject constructNode(Ruby ruby, Node node) {
74
+ if (node == null) return ruby.getNil();
75
+ // this is slow; need a way to cache nokogiri classes/modules somewhere
76
+ switch (node.getNodeType()) {
77
+ case Node.ELEMENT_NODE:
78
+ return new XmlElement(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Element"), node);
79
+ case Node.ATTRIBUTE_NODE:
80
+ return new XmlAttr(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Attr"), node);
81
+ case Node.TEXT_NODE:
82
+ return new XmlText(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Text"), node);
83
+ case Node.COMMENT_NODE:
84
+ return new XmlComment(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Comment"), node);
85
+ case Node.ENTITY_NODE:
86
+ return new XmlNode(ruby, getNokogiriClass(ruby, "Nokogiri::XML::EntityDecl"), node);
87
+ case Node.CDATA_SECTION_NODE:
88
+ return new XmlCdata(ruby, getNokogiriClass(ruby, "Nokogiri::XML::CDATA"), node);
89
+ case Node.DOCUMENT_NODE:
90
+ return new XmlDocument(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Document"), (Document) node);
91
+ default:
92
+ return new XmlNode(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Node"), node);
93
+ }
94
+ }
95
+
96
+ public static RubyClass getNokogiriClass(Ruby ruby, String name) {
97
+ RubyHash classCache = (RubyHash) ruby.getGlobalVariables().get(NokogiriService.nokogiriClassCacheGvarName);
98
+ IRubyObject rubyName = JavaUtil.convertJavaToUsableRubyObject(ruby, name);
99
+ return (RubyClass)classCache.fastARef(rubyName);
100
+ }
101
+
102
+ public static IRubyObject stringOrNil(Ruby ruby, String s) {
103
+ if (s == null)
104
+ return ruby.getNil();
105
+
106
+ return JavaUtil.convertJavaToUsableRubyObject(ruby, s);
107
+ }
108
+
109
+ public static IRubyObject stringOrBlank(Ruby ruby, String s) {
110
+ if (s == null) return ruby.newString();
111
+ return ruby.newString(s);
112
+ }
113
+
114
+ /**
115
+ * Convert <code>s</code> to a RubyString, or if s is null or
116
+ * empty return RubyNil.
117
+ */
118
+ public static IRubyObject nonEmptyStringOrNil(Ruby ruby, String s) {
119
+ if (s == null || s.length() == 0)
120
+ return ruby.getNil();
121
+
122
+ return ruby.newString(s);
123
+ }
124
+
125
+ /**
126
+ * Return the prefix of a qualified name like "prefix:local".
127
+ * Returns null if there is no prefix.
128
+ */
129
+ public static String getPrefix(String qName) {
130
+ if (qName == null) return null;
131
+
132
+ int pos = qName.indexOf(':');
133
+ if (pos > 0)
134
+ return qName.substring(0, pos);
135
+ else
136
+ return null;
137
+ }
138
+
139
+ /**
140
+ * Return the local part of a qualified name like "prefix:local".
141
+ * Returns <code>qName</code> if there is no prefix.
142
+ */
143
+ public static String getLocalPart(String qName) {
144
+ if (qName == null) return null;
145
+
146
+ int pos = qName.indexOf(':');
147
+ if (pos > 0)
148
+ return qName.substring(pos + 1);
149
+ else
150
+ return qName;
151
+ }
152
+
153
+ public static String getLocalNameForNamespace(String name) {
154
+ String localName = getLocalPart(name);
155
+ return ("xmlns".equals(localName)) ? null : localName;
156
+ }
157
+
158
+ protected static Charset utf8 = null;
159
+ protected static Charset getCharsetUTF8() {
160
+ if (utf8 == null) {
161
+ utf8 = Charset.forName("UTF-8");
162
+ }
163
+
164
+ return utf8;
165
+ }
166
+
167
+ /**
168
+ * Converts a RubyString in to a Java String. Assumes the
169
+ * RubyString is encoded as UTF-8. This is generally the case for
170
+ * RubyStrings created with getRuntime().newString("java string").
171
+ * It also seems to be the case for strings created within Ruby
172
+ * where $KCODE has not been set.
173
+ *
174
+ * Note that RubyString#toString() decodes the string data as
175
+ * ISO-8859-1 (See org.jruby.util.ByteList.java). This is not
176
+ * what you want if you have any multibyte characters in your
177
+ * UTF-8 string.
178
+ *
179
+ * FIXME: This really needs to be more robust in terms of
180
+ * detecting the encoding and properly converting to a Java
181
+ * String. It's unfortunate that RubyString#toString() doesn't do
182
+ * this for us.
183
+ */
184
+ public static String rubyStringToString(IRubyObject str) {
185
+ return rubyStringToString(str.convertToString());
186
+ }
187
+
188
+ public static String rubyStringToString(RubyString str) {
189
+ ByteList byteList = str.getByteList();
190
+ byte[] data = byteList.unsafeBytes();
191
+ int offset = byteList.begin();
192
+ int len = byteList.length();
193
+ ByteBuffer buf = ByteBuffer.wrap(data, offset, len);
194
+ return getCharsetUTF8().decode(buf).toString();
195
+ }
196
+
197
+ public static String getNodeCompletePath(Node node) {
198
+
199
+ Node cur, tmp, next;
200
+
201
+ // TODO: Rename buffer to path.
202
+ String buffer = "";
203
+ String sep;
204
+ String name;
205
+
206
+ int occur = 0;
207
+ boolean generic;
208
+
209
+ cur = node;
210
+
211
+ do {
212
+ name = "";
213
+ sep = "?";
214
+ occur = 0;
215
+ generic = false;
216
+
217
+ if(cur.getNodeType() == Node.DOCUMENT_NODE) {
218
+ if(buffer.startsWith("/")) break;
219
+
220
+ sep = "/";
221
+ next = null;
222
+ } else if(cur.getNodeType() == Node.ELEMENT_NODE) {
223
+ generic = false;
224
+ sep = "/";
225
+
226
+ name = cur.getLocalName();
227
+ if (name == null) name = cur.getNodeName();
228
+ if(cur.getNamespaceURI() != null) {
229
+ if(cur.getPrefix() != null) {
230
+ name = cur.getPrefix() + ":" + name;
231
+ } else {
232
+ generic = true;
233
+ name = "*";
234
+ }
235
+ }
236
+
237
+ next = cur.getParentNode();
238
+
239
+ /*
240
+ * Thumbler index computation
241
+ */
242
+
243
+ tmp = cur.getPreviousSibling();
244
+
245
+ while(tmp != null) {
246
+ if((tmp.getNodeType() == Node.ELEMENT_NODE) &&
247
+ (generic || fullNamesMatch(tmp, cur))) {
248
+ occur++;
249
+ }
250
+ tmp = tmp.getPreviousSibling();
251
+ }
252
+
253
+ if(occur == 0) {
254
+ tmp = cur.getNextSibling();
255
+
256
+ while(tmp != null && occur == 0) {
257
+ if((tmp.getNodeType() == Node.ELEMENT_NODE) &&
258
+ (generic || fullNamesMatch(tmp,cur))) {
259
+ occur++;
260
+ }
261
+ tmp = tmp.getNextSibling();
262
+ }
263
+
264
+ if(occur != 0) occur = 1;
265
+
266
+ } else {
267
+ occur++;
268
+ }
269
+ } else if(cur.getNodeType() == Node.COMMENT_NODE) {
270
+ sep = "/";
271
+ name = "comment()";
272
+ next = cur.getParentNode();
273
+
274
+ /*
275
+ * Thumbler index computation.
276
+ */
277
+
278
+ tmp = cur.getPreviousSibling();
279
+
280
+ while(tmp != null) {
281
+ if(tmp.getNodeType() == Node.COMMENT_NODE) {
282
+ occur++;
283
+ }
284
+ tmp = tmp.getPreviousSibling();
285
+ }
286
+
287
+ if(occur == 0) {
288
+ tmp = cur.getNextSibling();
289
+ while(tmp != null && occur == 0) {
290
+ if(tmp.getNodeType() == Node.COMMENT_NODE) {
291
+ occur++;
292
+ }
293
+ tmp = tmp.getNextSibling();
294
+ }
295
+ if(occur != 0) occur = 1;
296
+ } else {
297
+ occur = 1;
298
+ }
299
+
300
+ } else if(cur.getNodeType() == Node.TEXT_NODE ||
301
+ cur.getNodeType() == Node.CDATA_SECTION_NODE) {
302
+ // I'm here. gist:129
303
+ // http://gist.github.com/144923
304
+
305
+ sep = "/";
306
+ name = "text()";
307
+ next = cur.getParentNode();
308
+
309
+ /*
310
+ * Thumbler index computation.
311
+ */
312
+
313
+ tmp = cur.getPreviousSibling();
314
+ while(tmp != null) {
315
+ if(tmp.getNodeType() == Node.TEXT_NODE ||
316
+ tmp.getNodeType() == Node.CDATA_SECTION_NODE) {
317
+ occur++;
318
+ }
319
+ tmp = tmp.getPreviousSibling();
320
+ }
321
+
322
+ if(occur == 0) {
323
+ tmp = cur.getNextSibling();
324
+
325
+ while(tmp != null && occur == 0) {
326
+ if(tmp.getNodeType() == Node.TEXT_NODE ||
327
+ tmp.getNodeType() == Node.CDATA_SECTION_NODE) {
328
+ occur++;
329
+ }
330
+ tmp = tmp.getNextSibling();
331
+ }
332
+ } else {
333
+ occur++;
334
+ }
335
+
336
+ } else if(cur.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
337
+ sep = "/";
338
+ name = "processing-instruction('"+cur.getLocalName()+"')";
339
+ next = cur.getParentNode();
340
+
341
+ /*
342
+ * Thumbler index computation.
343
+ */
344
+
345
+ tmp = cur.getParentNode();
346
+
347
+ while(tmp != null) {
348
+ if(tmp.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
349
+ tmp.getLocalName().equals(cur.getLocalName())) {
350
+ occur++;
351
+ }
352
+ tmp = tmp.getPreviousSibling();
353
+ }
354
+
355
+ if(occur == 0) {
356
+ tmp = cur.getNextSibling();
357
+
358
+ while(tmp != null && occur == 0) {
359
+ if(tmp.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
360
+ tmp.getLocalName().equals(cur.getLocalName())){
361
+ occur++;
362
+ }
363
+ tmp = tmp.getNextSibling();
364
+ }
365
+
366
+ if(occur != 0) {
367
+ occur = 1;
368
+ }
369
+
370
+ } else {
371
+ occur++;
372
+ }
373
+
374
+ } else if(cur.getNodeType() == Node.ATTRIBUTE_NODE) {
375
+ sep = "/@";
376
+ name = cur.getLocalName();
377
+
378
+ if(cur.getNamespaceURI() != null) {
379
+ if(cur.getPrefix() != null) {
380
+ name = cur.getPrefix() + ":" + name;
381
+ }
382
+ }
383
+
384
+ next = ((Attr) cur).getOwnerElement();
385
+
386
+ } else {
387
+ next = cur.getParentNode();
388
+ }
389
+
390
+ if(occur == 0){
391
+ buffer = sep+name+buffer;
392
+ } else {
393
+ buffer = sep+name+"["+occur+"]"+buffer;
394
+ }
395
+
396
+ cur = next;
397
+
398
+ } while(cur != null);
399
+
400
+ return buffer;
401
+ }
402
+
403
+ protected static boolean compareTwoNodes(Node m, Node n) {
404
+ return nodesAreEqual(m.getLocalName(), n.getLocalName()) &&
405
+ nodesAreEqual(m.getPrefix(), n.getPrefix());
406
+ }
407
+
408
+ protected static boolean fullNamesMatch(Node a, Node b) {
409
+ return a.getNodeName().equals(b.getNodeName());
410
+ //return getFullName(a).equals(getFullName(b));
411
+ }
412
+
413
+ protected static String getFullName(Node n) {
414
+ String lname = n.getLocalName();
415
+ String prefix = n.getPrefix();
416
+ if (lname != null) {
417
+ if (prefix != null)
418
+ return prefix + ":" + lname;
419
+ else
420
+ return lname;
421
+ } else {
422
+ return n.getNodeName();
423
+ }
424
+ }
425
+
426
+ private static boolean nodesAreEqual(Object a, Object b) {
427
+ return (((a == null) && (a == null)) ||
428
+ (a != null) && (b != null) &&
429
+ (b.equals(a)));
430
+ }
431
+
432
+ public static String encodeJavaString(String s) {
433
+
434
+ // From entities.c
435
+ s = s.replaceAll("&", "&amp;");
436
+ s = s.replaceAll("<", "&lt;");
437
+ s = s.replaceAll(">", "&gt;");
438
+ // s = s.replaceAll("\"", "&quot;");
439
+ return s.replaceAll("\r", "&#13;");
440
+ }
441
+
442
+ public static String decodeJavaString(String s) {
443
+ s = s.replaceAll("&amp;", "&");
444
+ s = s.replaceAll("&lt;", "<");
445
+ s = s.replaceAll("&gt;", ">");
446
+ return s.replaceAll("&#13;", "\r");
447
+ }
448
+
449
+ public static boolean isXmlEscaped(String s) {
450
+ if (s == null) return true;
451
+ if (s.contains("<") || s.contains(">") || s.contains("\r")) return false;
452
+ if (s.contains("&") && !s.contains("&amp;")) return false;
453
+ return true;
454
+ }
455
+
456
+ public static String getNodeName(Node node) {
457
+ if(node == null) { System.out.println("node is null"); return ""; }
458
+ String name = node.getNodeName();
459
+ if(name == null) { System.out.println("name is null"); return ""; }
460
+ if(name.equals("#document")) {
461
+ return "document";
462
+ } else if(name.equals("#text")) {
463
+ return "text";
464
+ } else {
465
+ name = getLocalPart(name);
466
+ return (name == null) ? "" : name;
467
+ }
468
+ }
469
+
470
+ public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
471
+ public static boolean isNamespace(Node node) {
472
+ return (XMLNS_URI.equals(node.getNamespaceURI()) ||
473
+ isNamespace(node.getNodeName()));
474
+ }
475
+
476
+ public static boolean isNamespace(String nodeName) {
477
+ return (nodeName.equals("xmlns") || nodeName.startsWith("xmlns:"));
478
+ }
479
+
480
+ public static boolean isNonDefaultNamespace(Node node) {
481
+ return (isNamespace(node) && ! "xmlns".equals(node.getNodeName()));
482
+ }
483
+
484
+ public static boolean isXmlBase(String attrName) {
485
+ return "xml:base".equals(attrName) || "xlink:href".equals(attrName);
486
+ }
487
+
488
+ public static String newQName(String newPrefix, Node node) {
489
+ if(newPrefix == null) {
490
+ return node.getLocalName();
491
+ } else {
492
+ return newPrefix + ":" + node.getLocalName();
493
+ }
494
+ }
495
+
496
+ public static RubyArray nodeListToRubyArray(Ruby ruby, NodeList nodes) {
497
+ RubyArray n = RubyArray.newArray(ruby, nodes.getLength());
498
+ for(int i = 0; i < nodes.getLength(); i++) {
499
+ n.append(NokogiriHelpers.getCachedNodeOrCreate(ruby, nodes.item(i)));
500
+ }
501
+ return n;
502
+ }
503
+
504
+ public static RubyArray nodeArrayToRubyArray(Ruby ruby, Node[] nodes) {
505
+ RubyArray n = RubyArray.newArray(ruby, nodes.length);
506
+ for(int i = 0; i < nodes.length; i++) {
507
+ n.append(NokogiriHelpers.getCachedNodeOrCreate(ruby, nodes[i]));
508
+ }
509
+ return n;
510
+ }
511
+
512
+ public static RubyArray namedNodeMapToRubyArray(Ruby ruby, NamedNodeMap map) {
513
+ RubyArray n = RubyArray.newArray(ruby, map.getLength());
514
+ for(int i = 0; i < map.getLength(); i++) {
515
+ n.append(NokogiriHelpers.getCachedNodeOrCreate(ruby, map.item(i)));
516
+ }
517
+ return n;
518
+ }
519
+
520
+ public static String guessEncoding(Ruby ruby) {
521
+ String name = null;
522
+ if (name == null) name = System.getProperty("file.encoding");
523
+ if (name == null) name = "UTF-8";
524
+ return name;
525
+ }
526
+ }