/* | |
* $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.*; | |
/** | |
* 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 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. | |
*/ | |
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) ); | |
} | |
} | |
} |