/* * Copyright (c) OSGi Alliance (2002, 2006, 2007). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.osgi.service.indexer.impl.util; import java.io.PrintWriter; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.Map; import java.util.TreeMap; import java.util.Vector; /** * The Tag class represents a minimal XML tree. It consist of a named element * with a hashtable of named attributes. Methods are provided to walk the tree * and get its constituents. The content of a Tag is a list that contains String * objects or other Tag objects. */ public class Tag { Tag parent; String name; Map<String,String> attributes = new TreeMap<String,String>(); Vector<Object> content = new Vector<Object>(); Vector<String> comments = new Vector<String>(); static SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); /** * Construct a new Tag with a name. */ public Tag(String name) { this.name = name; } /** * Construct a new Tag with a name. */ public Tag(String name, Map<String,String> attributes) { this.name = name; this.attributes = attributes; } /** * Construct a new Tag with a name and a set of attributes. The attributes * are given as ( name, value ) ... */ public Tag(String name, String[] attributes) { this.name = name; for (int i = 0; i < attributes.length; i += 2) addAttribute(attributes[i], attributes[i + 1]); } /** * Construct a new Tag with a single string as content. */ public Tag(String name, String content) { this.name = name; addContent(content); } /** * Add a new attribute. */ public void addAttribute(String key, String value) { attributes.put(key, value); } /** * Add a new attribute. */ public void addAttribute(String key, Object value) { if (value == null) return; attributes.put(key, value.toString()); } /** * Add a new attribute. */ public void addAttribute(String key, int value) { attributes.put(key, Integer.toString(value)); } /** * Add a new date attribute. The date is formatted as the SimpleDateFormat * describes at the top of this class. */ public void addAttribute(String key, Date value) { attributes.put(key, format.format(value)); } /** * Add a new content string. */ public void addContent(String string) { content.addElement(string); } /** * Add a new content tag. */ public void addContent(Tag tag) { content.addElement(tag); tag.parent = this; } /** * Add a new content tag. */ public void addComment(String comment) { comments.addElement(comment); } /** * Return the name of the tag. */ public String getName() { return name; } /** * Return the attribute value. */ public String getAttribute(String key) { return attributes.get(key); } /** * Return the attribute value or a default if not defined. */ public String getAttribute(String key, String deflt) { String answer = getAttribute(key); return answer == null ? deflt : answer; } /** * Answer the attributes as a Dictionary object. */ public Map<String,String> getAttributes() { return attributes; } /** * Return the contents. */ public Vector<Object> getContents() { return content; } /** * Return the contents. */ public Vector<String> getComments() { return comments; } /** * Return a string representation of this Tag and all its children * recursively. */ public String toString() { StringWriter sw = new StringWriter(); print(Indent.NONE, new PrintWriter(sw)); return sw.toString(); } /** * Return only the tags of the first level of descendants that match the * name. */ public Vector<Object> getContents(String tag) { Vector<Object> out = new Vector<Object>(); for (Object o : content) { if (o instanceof Tag && ((Tag) o).getName().equals(tag)) out.addElement(o); } return out; } /** * Return the whole contents as a String (no tag info and attributes). */ public String getContentsAsString() { StringBuffer sb = new StringBuffer(); getContentsAsString(sb); return sb.toString(); } /** * convenient method to get the contents in a StringBuffer. */ public void getContentsAsString(StringBuffer sb) { for (Object o : content) { if (o instanceof Tag) ((Tag) o).getContentsAsString(sb); else sb.append(o.toString()); } } /** * Print the tag formatted to a PrintWriter. */ public void print(Indent indent, PrintWriter pw) { boolean empty = (content.size() + comments.size()) == 0; printOpen(indent, pw, empty); if (!empty) { printComments(indent, pw); printContents(indent, pw); printClose(indent, pw); } } public void printOpen(Indent indent, PrintWriter pw, boolean andClose) { indent.print(pw); pw.print('<'); pw.print(name); String quote = "\""; for (Map.Entry<String,String> e : attributes.entrySet()) { String key = e.getKey(); String value = escape(e.getValue()); pw.print(' '); pw.print(key); pw.print("="); pw.print(quote); pw.print(value.replaceAll("\"", """)); pw.print(quote); } if (andClose) pw.print("/>"); else pw.print('>'); } public void printComments(Indent indent, PrintWriter pw) { for (String comment : this.comments) { Indent nextIndent = indent.next(); nextIndent.print(pw); pw.print("<!-- "); pw.print(escape(comment)); pw.print(" -->"); } } public void printContents(Indent indent, PrintWriter pw) { for (Object content : this.content) { Indent nextIndent = indent.next(); if (content instanceof String) { formatted(pw, nextIndent, 60, escape((String) content)); } else if (content instanceof Tag) { Tag tag = (Tag) content; tag.print(nextIndent, pw); } } } public void printClose(Indent indent, PrintWriter pw) { indent.print(pw); pw.print("</"); pw.print(name); pw.print('>'); } /** * Convenience method to print a string nicely and does character conversion * to entities. */ void formatted(PrintWriter pw, Indent indent, int width, String s) { int pos = width + 1; s = s.trim(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (i == 0 || (Character.isWhitespace(c) && pos > width - 3)) { indent.print(pw); pos = 0; } switch (c) { case '<' : pw.print("<"); pos += 4; break; case '>' : pw.print(">"); pos += 4; break; case '&' : pw.print("&"); pos += 5; break; default : pw.print(c); pos++; break; } } } /** * Escape a string, do entity conversion. */ String escape(String s) { if (s == null) return "?null?"; StringBuffer sb = new StringBuffer(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '<' : sb.append("<"); break; case '>' : sb.append(">"); break; case '&' : sb.append("&"); break; default : sb.append(c); break; } } return sb.toString(); } /** * Make spaces. void spaces(PrintWriter pw, int n) { while (n-- > 0) * pw.print(' '); } */ /** * root/preferences/native/os */ public Tag[] select(String path) { return select(path, (Tag) null); } public Tag[] select(String path, Tag mapping) { Vector<Object> v = new Vector<Object>(); select(path, v, mapping); Tag[] result = new Tag[v.size()]; v.copyInto(result); return result; } void select(String path, Vector<Object> results, Tag mapping) { if (path.startsWith("//")) { int i = path.indexOf('/', 2); String name = path.substring(2, i < 0 ? path.length() : i); for (Object o : content) { if (o instanceof Tag) { Tag child = (Tag) o; if (match(name, child, mapping)) results.add(child); child.select(path, results, mapping); } } return; } if (path.length() == 0) { results.addElement(this); return; } int i = path.indexOf("/"); String elementName = path; String remainder = ""; if (i > 0) { elementName = path.substring(0, i); remainder = path.substring(i + 1); } for (Object o : content) { if (o instanceof Tag) { Tag child = (Tag) o; if (child.getName().equals(elementName) || elementName.equals("*")) child.select(remainder, results, mapping); } } } public boolean match(String search, Tag child, Tag mapping) { String target = child.getName(); String sn = null; String tn = null; if (search.equals("*")) return true; int s = search.indexOf(':'); if (s > 0) { sn = search.substring(0, s); search = search.substring(s + 1); } int t = target.indexOf(':'); if (t > 0) { tn = target.substring(0, t); target = target.substring(t + 1); } if (!search.equals(target)) // different tag names return false; if (mapping == null) { return (tn == null && sn == null) || (sn != null && sn.equals(tn)); } else { String suri = sn == null ? mapping.getAttribute("xmlns") : mapping.getAttribute("xmlns:" + sn); String turi = tn == null ? child.findRecursiveAttribute("xmlns") : child.findRecursiveAttribute("xmlns:" + tn); return turi == suri || (turi != null && suri != null && turi.equals(suri)); } } public String getString(String path) { String attribute = null; int index = path.indexOf("@"); if (index >= 0) { // attribute attribute = path.substring(index + 1); if (index > 0) { // prefix path path = path.substring(index - 1); // skip -1 } else path = ""; } Tag tags[] = select(path); StringBuffer sb = new StringBuffer(); for (int i = 0; i < tags.length; i++) { if (attribute == null) tags[i].getContentsAsString(sb); else sb.append(tags[i].getAttribute(attribute)); } return sb.toString(); } public String getStringContent() { StringBuffer sb = new StringBuffer(); for (Object c : content) { if (!(c instanceof Tag)) sb.append(c); } return sb.toString(); } public String getNameSpace() { return getNameSpace(name); } public String getNameSpace(String name) { int index = name.indexOf(':'); if (index > 0) { String ns = name.substring(0, index); return findRecursiveAttribute("xmlns:" + ns); } else return findRecursiveAttribute("xmlns"); } public String findRecursiveAttribute(String name) { String value = getAttribute(name); if (value != null) return value; if (parent != null) return parent.findRecursiveAttribute(name); return null; } public String getLocalName() { int index = name.indexOf(':'); if (index <= 0) return name; return name.substring(index + 1); } public void rename(String string) { name = string; } public static void convert(Collection<Map<String,String>> c, String type, Tag parent) { for (Map<String,String> map : c) { parent.addContent(new Tag(type, map)); } } }