/*
* Copyright 2004-2012 the Seasar Foundation and the Others.
*
* 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.seasar.mayaa.impl.engine.specification;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.seasar.mayaa.engine.specification.Namespace;
import org.seasar.mayaa.engine.specification.PrefixMapping;
import org.seasar.mayaa.engine.specification.URI;
import org.seasar.mayaa.impl.CONST_IMPL;
import org.seasar.mayaa.impl.util.ReferenceCache;
import org.seasar.mayaa.impl.util.StringUtil;
import org.seasar.mayaa.impl.util.collection.NullIterator;
/**
* @author Masataka Kurihara (Gluegent, Inc.)
*/
public class NamespaceImpl implements Namespace {
private static final long serialVersionUID = -3738362040016319461L;
private static final Log LOG = LogFactory.getLog(NamespaceImpl.class);
private static ReferenceCache/*<Namespace>*/ _cache =
new ReferenceCache(Namespace.class);
public static Namespace getInstance(Namespace namespace) {
return getInstance(serialize(namespace));
}
public static Namespace getInstance(String serializeKey) {
synchronized(_cache) {
// キャッシュされたシングルトンを返す
for (Iterator it = _cache.iterator(); it.hasNext(); ) {
NamespaceImpl space = (NamespaceImpl) it.next();
if (space.getSerializeKey().equals(serializeKey)) {
return space;
}
}
Namespace namespace = deserialize(serializeKey);
if (_cache.contains(namespace) == false) {
_cache.add(namespace);
}
return namespace;
}
}
public static Namespace copyOf(Namespace namespace) {
if (namespace == null) {
return namespace;
}
Namespace parent = namespace.getParentSpace();
if (parent != null) {
String key = serialize(parent);
parent = getInstance(key);
}
NamespaceImpl result = new NamespaceImpl();
result.setParentSpace(parent);
for (Iterator it = namespace.iteratePrefixMapping(false); it.hasNext(); ) {
PrefixMapping mapping = (PrefixMapping) it.next();
result.addPrefixMapping(mapping.getPrefix(), mapping.getNamespaceURI());
}
result.setDefaultNamespaceURI(namespace.getDefaultNamespaceURI());
if (namespace instanceof NamespaceImpl) {
result._serializeKey = ((NamespaceImpl)namespace)._serializeKey;
}
return result;
}
private transient Namespace _parentSpace;
private transient Set/*<PrefixMapping>*/ _mappings;
private transient PrefixMapping _defaultNamespaceMapping;
private String _serializeKey;
private transient boolean _needDeserialize;
public void setParentSpace(Namespace parent) {
/*
if (parent == null) {
throw new IllegalArgumentException();
}
*/
if (parent != null) {
if (_parentSpace == parent || parent.equals(_parentSpace)) {
return;
}
}
_parentSpace = parent;
_serializeKey = null;
}
public Namespace getParentSpace() {
doDeserialize();
return _parentSpace;
}
public void addPrefixMapping(String prefix, URI namespaceURI) {
if (prefix == null || StringUtil.isEmpty(namespaceURI)) {
throw new IllegalArgumentException();
}
synchronized (this) {
doDeserialize();
if (_mappings == null) {
// テンプレートによってxmlnsの記述順が異なったとしても、
// 同一のものを保証するためにソートする
_mappings = new TreeSet(_prefixMappingComparator);
}
PrefixMapping mapping =
SpecificationUtil.createPrefixMapping(prefix, namespaceURI);
if (_mappings.contains(mapping) == false) {
_mappings.add(mapping);
_serializeKey = null;
} else {
if (LOG.isWarnEnabled()) {
LOG.warn(StringUtil.getMessage(NamespaceImpl.class, 0,
mapping.toString()));
}
}
}
}
public void setDefaultNamespaceURI(URI namespaceURI) {
doDeserialize();
if (namespaceURI == null) {
if (_defaultNamespaceMapping != null) {
_defaultNamespaceMapping = null;
_serializeKey = null;
}
return;
}
for (Iterator it = iteratePrefixMapping(true); it.hasNext();) {
PrefixMapping mapping = (PrefixMapping) it.next();
if (mapping.getNamespaceURI().equals(namespaceURI)) {
if (mapping.equals(_defaultNamespaceMapping) == false) {
_defaultNamespaceMapping = mapping;
_serializeKey = null;
}
return;
}
}
if (namespaceURI.equals(CONST_IMPL.URI_HTML)) {
if (SpecificationUtil.HTML_DEFAULT_PREFIX_MAPPING.equals(
_defaultNamespaceMapping) == false) {
_defaultNamespaceMapping =
SpecificationUtil.HTML_DEFAULT_PREFIX_MAPPING;
_serializeKey = null;
}
return;
}
if (namespaceURI.equals(CONST_IMPL.URI_XHTML)) {
if (SpecificationUtil.XHTML_DEFAULT_PREFIX_MAPPING.equals(
_defaultNamespaceMapping) == false) {
_defaultNamespaceMapping =
SpecificationUtil.XHTML_DEFAULT_PREFIX_MAPPING;
_serializeKey = null;
}
return;
}
_defaultNamespaceMapping =
SpecificationUtil.createPrefixMapping("", namespaceURI);
_serializeKey = null;
}
protected void setDefaultNamespaceMapping(
PrefixMapping defaultNamespaceMapping) {
_defaultNamespaceMapping = defaultNamespaceMapping;
}
protected PrefixMapping getDefaultNamespaceMapping() {
doDeserialize();
return _defaultNamespaceMapping;
}
public URI getDefaultNamespaceURI() {
doDeserialize();
if (_defaultNamespaceMapping == null) {
return null;
}
return _defaultNamespaceMapping.getNamespaceURI();
}
protected PrefixMapping getMapping(
boolean fromPrefix, String test, boolean all) {
if (test == null) {
throw new IllegalArgumentException();
}
doDeserialize();
if (_defaultNamespaceMapping != null) {
if (fromPrefix) {
if ("".equals(test) || _defaultNamespaceMapping.getPrefix().equals(test)) {
return _defaultNamespaceMapping;
}
} else {
if (_defaultNamespaceMapping.getNamespaceURI().equals(test)) {
return _defaultNamespaceMapping;
}
}
}
for (Iterator it = iteratePrefixMapping(all); it.hasNext();) {
PrefixMapping mapping = (PrefixMapping) it.next();
String value = fromPrefix
? mapping.getPrefix() : mapping.getNamespaceURI().getValue();
if (test.equals(value)) {
return mapping;
}
}
if (CONST_IMPL.URI_XML.equals(test)) {
return SpecificationUtil.XML_DEFAULT_PREFIX_MAPPING;
}
return null;
}
public PrefixMapping getMappingFromPrefix(String prefix, boolean all) {
if (prefix == null) {
prefix = "";
}
return getMapping(true, prefix, all);
}
public PrefixMapping getMappingFromURI(
URI namespaceURI, boolean all) {
if (StringUtil.isEmpty(namespaceURI)) {
throw new IllegalArgumentException();
}
return getMapping(false, namespaceURI.getValue(), all);
}
public Iterator iteratePrefixMapping(boolean all) {
if (all && getParentSpace() != null) {
return new AllNamespaceIterator(this);
}
doDeserialize();
if (_mappings != null) {
return _mappings.iterator();
}
return NullIterator.getInstance();
}
public boolean addedMapping() {
doDeserialize();
if (_mappings == null) {
return false;
}
synchronized (_mappings) {
return _mappings.size() > 0;
}
}
protected String getSerializeKey() {
doDeserialize();
serialize(this);
return _serializeKey;
}
protected static String serialize(Namespace instance) {
if (instance instanceof NamespaceImpl == false) {
throw new IllegalStateException();
}
NamespaceImpl impl = (NamespaceImpl)instance;
if (impl._serializeKey != null) {
return impl._serializeKey;
}
List spaces = new ArrayList();
Namespace parent = instance.getParentSpace();
while (parent != null) {
spaces.add(parent);
parent = parent.getParentSpace();
}
StringBuffer buffer = new StringBuffer();
for (int i = spaces.size()-1; i >= 0; i--) {
Namespace current = (Namespace)spaces.get(i);
if (current instanceof NamespaceImpl) {
NamespaceImpl currentImpl = (NamespaceImpl) current;
buffer.append(currentImpl.namespaceToString());
} else {
throw new IllegalStateException();
}
}
buffer.append(impl.namespaceToString());
String result = buffer.toString();
impl._serializeKey = result;
return result;
}
protected static NamespaceImpl deserialize(String serializeData) {
String[] lines = serializeData.split("\n");
Namespace parent = null;
NamespaceImpl current = null;
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line.startsWith("/")) {
line = line.substring("/".length());
if (current != null) {
synchronized(_cache) {
if (_cache.contains(current) == false) {
_cache.add(current);
}
}
parent = current;
}
current = new NamespaceImpl();
if (String.valueOf((Object)null).equals(line) == false) {
current._defaultNamespaceMapping =
SpecificationUtil.createPrefixMapping(line);
}
if (parent != null) {
current.setParentSpace(parent);
}
} else if (line.startsWith("\t") && current != null) {
line = line.substring("\t".length());
// パフォーマンス重視のためにaddPrefixMappingを呼ばない
if (current._mappings == null) {
current._mappings = new TreeSet(_prefixMappingComparator);
}
current._mappings.add(SpecificationUtil.createPrefixMapping(line));
}
}
return current;
}
private String namespaceToString() {
StringBuffer sb = new StringBuffer();
sb.append("/").append(_defaultNamespaceMapping).append("\n");
for (Iterator it = iteratePrefixMapping(false); it.hasNext(); ) {
sb.append("\t").append(it.next()).append("\n");
}
return sb.toString();
}
public String toString() {
return namespaceToString();
}
public boolean equals(Object obj) {
if (obj != null && obj.getClass().equals(NamespaceImpl.class)) {
NamespaceImpl other = (NamespaceImpl)obj;
String otherKey = other.getSerializeKey();
return getSerializeKey().equals(otherKey);
}
return false;
}
// TODO hashCodeを実装する
private void writeObject(java.io.ObjectOutputStream out)
throws IOException {
if (getClass() == NamespaceImpl.class) {
getSerializeKey();
out.defaultWriteObject();
}
}
protected void doDeserialize() {
if (_needDeserialize) {
_needDeserialize = false;
NamespaceImpl current = deserialize(getSerializeKey());
_parentSpace = current._parentSpace;
_defaultNamespaceMapping = current._defaultNamespaceMapping;
_mappings = new TreeSet(_prefixMappingComparator);
for (Iterator it = current.iteratePrefixMapping(false)
; it.hasNext(); ) {
_mappings.add(it.next());
}
}
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
if (getClass() == NamespaceImpl.class) {
in.defaultReadObject();
_needDeserialize = true;
}
}
// support class -------------------------------------------------
protected static class AllNamespaceIterator implements Iterator {
private Namespace _current;
private Iterator _it;
public AllNamespaceIterator(Namespace current) {
if (current == null) {
throw new IllegalArgumentException();
}
_current = current;
_it = current.iteratePrefixMapping(false);
}
public boolean hasNext() {
while (_it != null) {
boolean ret = _it.hasNext();
if (ret) {
return true;
}
if (_current != null && (_current = _current.getParentSpace()) != null) {
_it = _current.iteratePrefixMapping(false);
} else {
_it = null;
}
}
return false;
}
public Object next() {
if (hasNext()) {
return _it.next();
}
throw new NoSuchElementException();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
protected static final Comparator _prefixMappingComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if (o1 instanceof PrefixMapping && o2 instanceof PrefixMapping) {
PrefixMapping mapping1 = (PrefixMapping)o1;
PrefixMapping mapping2 = (PrefixMapping)o2;
URI ns1 = mapping1.getNamespaceURI();
URI ns2 = mapping2.getNamespaceURI();
int result = 0;
if (ns1 == null) {
if (ns2 != null) {
result = -1;
}
} else if (ns2 == null) {
result = 1;
} else {
result = ns1.compareTo(ns2);
}
if (result == 0) {
String prefix1 = mapping1.getPrefix();
String prefix2 = mapping2.getPrefix();
if (prefix1 == null) {
prefix1 = "";
}
if (prefix2 == null) {
prefix2 = "";
}
result = prefix1.compareTo(prefix2);
}
return result;
}
throw new IllegalStateException();
}
public boolean equals(Object obj) {
return obj == this;
}
public int hashCode() {
return super.hashCode();
}
};
}