1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
package nokogiri;
import static nokogiri.internals.NokogiriHelpers.getLocalNameForNamespace;
import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
import static nokogiri.internals.NokogiriHelpers.getPrefix;
import static nokogiri.internals.NokogiriHelpers.isNamespace;
import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
import org.jruby.RubyString;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
/**
* Class for Nokogiri::XML::DocumentFragment
*
* @author sergio
* @author Yoko Harada <yokolet@gmail.com>
*/
@JRubyClass(name = "Nokogiri::XML::DocumentFragment", parent = "Nokogiri::XML::Node")
public class XmlDocumentFragment extends XmlNode
{
public
XmlDocumentFragment(Ruby ruby)
{
this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::DocumentFragment"));
}
public
XmlDocumentFragment(Ruby ruby, RubyClass klazz)
{
super(ruby, klazz);
}
@JRubyMethod(name = "new", meta = true, required = 1, optional = 3)
public static IRubyObject
rbNew(ThreadContext context, IRubyObject cls, IRubyObject[] args, Block block)
{
if (args.length < 1) {
throw context.runtime.newArgumentError(args.length, 1);
}
if (!(args[0] instanceof XmlDocument)) {
throw context.runtime.newArgumentError("first parameter must be a Nokogiri::XML::Document instance");
}
XmlDocument doc = (XmlDocument) args[0];
// make wellformed fragment, ignore invalid namespace, or add appropriate namespace to parse
if (args.length > 1 && args[1] instanceof RubyString) {
final RubyString arg1 = (RubyString) args[1];
if (XmlDocumentFragment.isTag(arg1)) {
args[1] = RubyString.newString(context.runtime, addNamespaceDeclIfNeeded(doc, rubyStringToString(arg1)));
}
}
XmlDocumentFragment fragment = (XmlDocumentFragment) NokogiriService.XML_DOCUMENT_FRAGMENT_ALLOCATOR.allocate(
context.runtime, (RubyClass)cls);
fragment.setDocument(context, doc);
fragment.setNode(context.runtime, doc.getDocument().createDocumentFragment());
Helpers.invoke(context, fragment, "initialize", args, block);
return fragment;
}
private static final ByteList TAG_BEG = ByteList.create("<");
private static final ByteList TAG_END = ByteList.create(">");
private static boolean
isTag(final RubyString str)
{
return str.getByteList().startsWith(TAG_BEG) && str.getByteList().endsWith(TAG_END);
}
private static boolean
isNamespaceDefined(String qName, NamedNodeMap nodeMap)
{
if (isNamespace(qName.intern())) { return true; }
for (int i = 0; i < nodeMap.getLength(); i++) {
Attr attr = (Attr)nodeMap.item(i);
if (isNamespace(attr.getNodeName())) {
String localPart = getLocalNameForNamespace(attr.getNodeName(), null);
if (getPrefix(qName).equals(localPart)) {
return true;
}
}
}
return false;
}
private static final Pattern QNAME_RE = Pattern.compile("[^</:>\\s]+:[^</:>=\\s]+");
private static final Pattern START_TAG_RE = Pattern.compile("<[^</>]+>");
private static String
addNamespaceDeclIfNeeded(XmlDocument doc, String tags)
{
if (doc.getDocument() == null) { return tags; }
if (doc.getDocument().getDocumentElement() == null) { return tags; }
Matcher matcher = START_TAG_RE.matcher(tags);
Map<CharSequence, CharSequence> rewriteTable = null;
while (matcher.find()) {
String start_tag = matcher.group();
Matcher matcher2 = QNAME_RE.matcher(start_tag);
while (matcher2.find()) {
String qName = matcher2.group();
NamedNodeMap nodeMap = doc.getDocument().getDocumentElement().getAttributes();
if (isNamespaceDefined(qName, nodeMap)) {
CharSequence namespaceDecl = getNamespaceDecl(getPrefix(qName), nodeMap);
if (namespaceDecl != null) {
if (rewriteTable == null) { rewriteTable = new HashMap(8, 1); }
StringBuilder str = new StringBuilder(qName.length() + namespaceDecl.length() + 3);
String key = str.append('<').append(qName).append('>').toString();
str.setCharAt(key.length() - 1, ' '); // (last) '>' -> ' '
rewriteTable.put(key, str.append(namespaceDecl).append('>'));
}
}
}
}
if (rewriteTable != null) {
for (Map.Entry<CharSequence, CharSequence> e : rewriteTable.entrySet()) {
tags = tags.replace(e.getKey(), e.getValue());
}
}
return tags;
}
private static CharSequence
getNamespaceDecl(final String prefix, NamedNodeMap nodeMap)
{
for (int i = 0; i < nodeMap.getLength(); i++) {
Attr attr = (Attr) nodeMap.item(i);
if (prefix.equals(attr.getLocalName())) {
return new StringBuilder().
append(attr.getName()).append('=').append('"').append(attr.getValue()).append('"');
}
}
return null;
}
@Override
public void
relink_namespace(ThreadContext context)
{
relink_namespace(context, getChildren());
}
}
|