| /* |
| * $Id: Tag.java 44 2007-07-13 20:49:41Z hargrave@us.ibm.com $ |
| * |
| * 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.impl.bundle.obr.resource; |
| |
| import java.io.*; |
| import java.text.SimpleDateFormat; |
| import java.util.*; |
| |
| import aQute.bnd.annotation.ProviderType; |
| |
| /** |
| * 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. |
| */ |
| @ProviderType |
| public class Tag { |
| Tag parent; |
| String name; |
| Map attributes = new TreeMap(); |
| Vector content = new Vector(); |
| |
| 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 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; |
| } |
| |
| /** |
| * Return the name of the tag. |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Return the attribute value. |
| */ |
| public String getAttribute(String key) { |
| return (String) 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 getAttributes() { |
| return attributes; |
| } |
| |
| /** |
| * Return the contents. |
| */ |
| public Vector getContents() { |
| return content; |
| } |
| |
| /** |
| * Return a string representation of this Tag and all its children |
| * recursively. |
| */ |
| @Override |
| public String toString() { |
| StringWriter sw = new StringWriter(); |
| print(0, new PrintWriter(sw)); |
| return sw.toString(); |
| } |
| |
| /** |
| * Return only the tags of the first level of descendants that match the |
| * name. |
| */ |
| public Vector getContents(String tag) { |
| Vector out = new Vector(); |
| for (Enumeration e = content.elements(); e.hasMoreElements();) { |
| Object o = e.nextElement(); |
| 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 (Enumeration e = content.elements(); e.hasMoreElements();) { |
| Object o = e.nextElement(); |
| if (o instanceof Tag) |
| ((Tag) o).getContentsAsString(sb); |
| else |
| sb.append(o.toString()); |
| } |
| } |
| |
| /** |
| * Print the tag formatted to a PrintWriter. |
| */ |
| public void print(int indent, PrintWriter pw) { |
| pw.print("\n"); |
| spaces(pw, indent); |
| pw.print('<'); |
| pw.print(name); |
| |
| for (Iterator e = attributes.keySet().iterator(); e.hasNext();) { |
| String key = (String) e.next(); |
| String value = escape((String) attributes.get(key)); |
| pw.print(' '); |
| pw.print(key); |
| pw.print("="); |
| String quote = "'"; |
| if (value.indexOf(quote) >= 0) |
| quote = "\""; |
| pw.print(quote); |
| pw.print(value); |
| pw.print(quote); |
| } |
| |
| if (content.size() == 0) |
| pw.print('/'); |
| else { |
| pw.print('>'); |
| for (Enumeration e = content.elements(); e.hasMoreElements();) { |
| Object content = e.nextElement(); |
| if (content instanceof String) { |
| formatted(pw, indent + 2, 60, escape((String) content)); |
| } |
| else if (content instanceof Tag) { |
| Tag tag = (Tag) content; |
| tag.print(indent + 2, pw); |
| } |
| } |
| pw.print("\n"); |
| spaces(pw, indent); |
| pw.print("</"); |
| pw.print(name); |
| } |
| pw.print('>'); |
| } |
| |
| /** |
| * Convenience method to print a string nicely and does character conversion |
| * to entities. |
| */ |
| void formatted(PrintWriter pw, int left, 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)) { |
| pw.print("\n"); |
| spaces(pw, left); |
| 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 v = new Vector(); |
| select(path, v, mapping); |
| Tag[] result = new Tag[v.size()]; |
| v.copyInto(result); |
| return result; |
| } |
| |
| void select(String path, Vector results, Tag mapping) { |
| if (path.startsWith("//")) { |
| int i = path.indexOf('/', 2); |
| String name = path.substring(2, i < 0 ? path.length() : i); |
| |
| for (Enumeration e = content.elements(); e.hasMoreElements();) { |
| Object o = e.nextElement(); |
| 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 (Enumeration e = content.elements(); e.hasMoreElements();) { |
| Object o = e.nextElement(); |
| 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 == sn || (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 (Enumeration e = content.elements(); e.hasMoreElements();) { |
| Object c = e.nextElement(); |
| 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 c, String type, Tag parent ) { |
| for ( Iterator i=c.iterator(); i.hasNext(); ) { |
| Map map = (Map) i.next(); |
| parent.addContent( new Tag(type, map) ); |
| } |
| } |
| |
| } |