com.google.api.client.xml.XmlNamespaceDictionary Maven / Gradle / Ivy
Show all versions of google-api-client Show documentation
/*
* Copyright (c) 2010 Google Inc.
*
* 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 com.google.api.client.xml;
import com.google.api.client.util.DataUtil;
import com.google.api.client.util.DateTime;
import com.google.api.client.util.FieldInfo;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Thread-safe XML namespace dictionary that provides a one-to-one map of namespace alias to URI.
*
*
* A namespace alias is uniquely mapped to a single namespace URI, and a namespace URI is uniquely
* mapped to a single namespace alias. In other words, it is not possible to have duplicates.
*
*
*
* Sample usage:
*
*
{@code
static final XmlNamespaceDictionary DICTIONARY = new XmlNamespaceDictionary()
.add("", "http://www.w3.org/2005/Atom")
.add("activity", "http://activitystrea.ms/spec/1.0/")
.add("georss", "http://www.georss.org/georss")
.add("media", "http://search.yahoo.com/mrss/")
.add("thr", "http://purl.org/syndication/thread/1.0");
*}
*
*
* Upgrade warning: in prior version 1.2 there was a field {@code namespaceAliasToUriMap}, which has
* now been made inaccessible.
*
* @since 1.0
* @author Yaniv Inbar
*/
public final class XmlNamespaceDictionary {
/**
* Map from XML namespace alias (or {@code ""} for the default namespace) to XML namespace URI.
*/
private final HashMap namespaceAliasToUriMap = Maps.newHashMap();
/**
* Map from XML namespace URI to XML namespace alias (or {@code ""} for the default namespace).
*/
private final HashMap namespaceUriToAliasMap = Maps.newHashMap();
/**
* Returns the namespace alias (or {@code ""} for the default namespace) for the given namespace
* URI.
*
* @param uri namespace URI
* @since 1.3
*/
public synchronized String getAliasForUri(String uri) {
return namespaceUriToAliasMap.get(Preconditions.checkNotNull(uri));
}
/**
* Returns the namespace URI for the given namespace alias (or {@code ""} for the default
* namespace).
*
* @param alias namespace alias (or {@code ""} for the default namespace)
* @since 1.3
*/
public synchronized String getUriForAlias(String alias) {
return namespaceAliasToUriMap.get(Preconditions.checkNotNull(alias));
}
/**
* Returns an unmodified set of map entries for the map from namespace alias (or {@code ""} for
* the default namespace) to namespace URI.
*
* @since 1.3
*/
public synchronized Map getAliasToUriMap() {
return Collections.unmodifiableMap(namespaceAliasToUriMap);
}
/**
* Returns an unmodified set of map entries for the map from namespace URI to namespace alias (or
* {@code ""} for the default namespace).
*
* @since 1.3
*/
public synchronized Map getUriToAliasMap() {
return Collections.unmodifiableMap(namespaceUriToAliasMap);
}
/**
* Adds a known namespace of the given alias and URI.
*
* @param alias alias
* @param uri namespace URI
* @deprecated use {@link #set(String, String)} (scheduled to be removed in 1.4)
*/
@Deprecated
public void addNamespace(String alias, String uri) {
set(alias, uri);
}
/**
* Adds a namespace of the given alias and URI.
*
*
* If the uri is {@code null}, the namespace alias will be removed. Similarly, if the alias is
* {@code null}, the namespace URI will be removed. Otherwise, if the alias is already mapped to a
* different URI, it will be remapped to the new URI. Similarly, if a URI is already mapped to a
* different alias, it will be remapped to the new alias.
*
*
* @param alias alias or {@code null} to remove the namespace URI
* @param uri namespace URI or {@code null} to remove the namespace alias
* @return this namespace dictionary
* @since 1.3
*/
public synchronized XmlNamespaceDictionary set(String alias, String uri) {
String previousUri = null;
String previousAlias = null;
if (uri == null) {
if (alias != null) {
previousUri = namespaceAliasToUriMap.remove(alias);
}
} else if (alias == null) {
previousAlias = namespaceUriToAliasMap.remove(uri);
} else {
previousUri = namespaceAliasToUriMap.put(
Preconditions.checkNotNull(alias), Preconditions.checkNotNull(uri));
if (!uri.equals(previousUri)) {
previousAlias = namespaceUriToAliasMap.put(uri, alias);
} else {
previousUri = null;
}
}
if (previousUri != null) {
namespaceUriToAliasMap.remove(previousUri);
}
if (previousAlias != null) {
namespaceAliasToUriMap.remove(previousAlias);
}
return this;
}
/**
* Shows a debug string representation of an element data object of key/value pairs.
*
* @param element element data object ({@link GenericXml}, {@link Map}, or any object with public
* fields)
* @param elementName optional XML element local name prefixed by its namespace alias -- for
* example {@code "atom:entry"} -- or {@code null} to make up something
*/
public String toStringOf(String elementName, Object element) {
try {
StringWriter writer = new StringWriter();
XmlSerializer serializer = Xml.createSerializer();
serializer.setOutput(writer);
serialize(serializer, elementName, element, false);
return writer.toString();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Shows a debug string representation of an element data object of key/value pairs.
*
* @param element element data object ({@link GenericXml}, {@link Map}, or any object with public
* fields)
* @param elementNamespaceUri XML namespace URI or {@code null} for no namespace
* @param elementLocalName XML local name
* @throws IOException I/O exception
*/
public void serialize(
XmlSerializer serializer, String elementNamespaceUri, String elementLocalName, Object element)
throws IOException {
serialize(serializer, elementNamespaceUri, elementLocalName, element, true);
}
/**
* Shows a debug string representation of an element data object of key/value pairs.
*
* @param element element data object ({@link GenericXml}, {@link Map}, or any object with public
* fields)
* @param elementName XML element local name prefixed by its namespace alias
* @throws IOException I/O exception
*/
public void serialize(XmlSerializer serializer, String elementName, Object element)
throws IOException {
serialize(serializer, elementName, element, true);
}
private void serialize(XmlSerializer serializer, String elementNamespaceUri,
String elementLocalName, Object element, boolean errorOnUnknown) throws IOException {
startDoc(serializer, element, errorOnUnknown, elementNamespaceUri).serialize(
serializer, elementNamespaceUri, elementLocalName);
serializer.endDocument();
}
private void serialize(
XmlSerializer serializer, String elementName, Object element, boolean errorOnUnknown)
throws IOException {
startDoc(serializer, element, errorOnUnknown, null).serialize(serializer, elementName);
serializer.endDocument();
}
private ElementSerializer startDoc(
XmlSerializer serializer, Object element, boolean errorOnUnknown, String extraNamespace)
throws IOException {
serializer.startDocument(null, null);
SortedSet aliases = new TreeSet();
computeAliases(element, aliases);
boolean foundExtra = extraNamespace == null;
for (String alias : aliases) {
String uri = getUriForAlias(alias);
serializer.setPrefix(alias, uri);
if (!foundExtra && uri.equals(extraNamespace)) {
foundExtra = true;
}
}
if (!foundExtra) {
for (Map.Entry entry : getAliasToUriMap().entrySet()) {
if (extraNamespace.equals(entry.getValue())) {
serializer.setPrefix(entry.getKey(), extraNamespace);
break;
}
}
}
return new ElementSerializer(element, errorOnUnknown);
}
private void computeAliases(Object element, SortedSet aliases) {
for (Map.Entry entry : DataUtil.mapOf(element).entrySet()) {
Object value = entry.getValue();
if (value != null) {
String name = entry.getKey();
if (!"text()".equals(name)) {
int colon = name.indexOf(':');
boolean isAttribute = name.charAt(0) == '@';
if (colon != -1 || !isAttribute) {
String alias = colon == -1 ? "" : name.substring(name.charAt(0) == '@' ? 1 : 0, colon);
aliases.add(alias);
}
if (!isAttribute && !FieldInfo.isPrimitive(value)) {
if (value instanceof Collection>) {
for (Object subValue : (Collection>) value) {
computeAliases(subValue, aliases);
}
} else {
computeAliases(value, aliases);
}
}
}
}
}
}
class ElementSerializer {
private final boolean errorOnUnknown;
Object textValue = null;
final List attributeNames = new ArrayList();
final List