/*
* Copyright (c) 2010-2013 Evolveum
*
* 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.evolveum.midpoint.prism.xml;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import com.evolveum.midpoint.prism.xml.DynamicNamespacePrefixMapper;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
/**
* Maps namespaces to preferred prefixes. Should be used through the code to
* avoid generation of prefixes.
*
* Although this is usually used as singleton (static), it can also be instantiated to locally
* override some namespace mappings. This is useful for prefixes like "tns" (schema) or "ri" (resource schema).
*
* @see MID-349
*
* @author Igor Farinic
* @author Radovan Semancik
*
*/
public class GlobalDynamicNamespacePrefixMapper extends NamespacePrefixMapper implements DynamicNamespacePrefixMapper, DebugDumpable {
private static final Map<String, String> globalNamespacePrefixMap = new HashMap<String, String>();
private Map<String, String> localNamespacePrefixMap = new HashMap<String, String>();
private String defaultNamespace = null;
private boolean alwaysExplicit = false;
private final Set<String> prefixesDeclaredByDefault = new HashSet<>();
public String getDefaultNamespace() {
return defaultNamespace;
}
public synchronized void setDefaultNamespace(String defaultNamespace) {
this.defaultNamespace = defaultNamespace;
}
@Override
public boolean isAlwaysExplicit() {
return alwaysExplicit;
}
@Override
public void setAlwaysExplicit(boolean alwaysExplicit) {
this.alwaysExplicit = alwaysExplicit;
}
@Override
public void addDeclaredByDefault(String prefix) {
prefixesDeclaredByDefault.add(prefix);
}
@Override
public synchronized Map<String, String> getNamespacesDeclaredByDefault() {
Map<String, String> retval = new HashMap<>();
for (Map.Entry<String, String> entry : globalNamespacePrefixMap.entrySet()) {
String prefix = entry.getValue();
if (prefixesDeclaredByDefault.contains(prefix)) {
retval.put(prefix, entry.getKey());
}
}
return retval;
}
@Override
public synchronized void registerPrefix(String namespace, String prefix, boolean isDefaultNamespace) {
registerPrefixGlobal(namespace, prefix);
if (isDefaultNamespace || prefix == null) {
defaultNamespace = namespace;
}
}
private static synchronized void registerPrefixGlobal(String namespace, String prefix) {
globalNamespacePrefixMap.put(namespace, prefix);
}
@Override
public synchronized void registerPrefixLocal(String namespace, String prefix) {
localNamespacePrefixMap.put(namespace, prefix);
}
@Override
public String getPrefix(String namespace) {
if (defaultNamespace != null && defaultNamespace.equals(namespace) && !alwaysExplicit) {
return "";
}
return getPrefixExplicit(namespace);
}
public synchronized String getPrefixExplicit(String namespace) {
String prefix = localNamespacePrefixMap.get(namespace);
if (prefix == null) {
return getPreferredPrefix(namespace);
}
return prefix;
}
@Override
public QName setQNamePrefix(QName qname) {
String namespace = qname.getNamespaceURI();
String prefix = getPrefix(namespace);
if (prefix == null) {
return qname;
}
return new QName(qname.getNamespaceURI(),qname.getLocalPart(),prefix);
}
@Override
public QName setQNamePrefixExplicit(QName qname) {
String namespace = qname.getNamespaceURI();
String prefix = getPrefixExplicit(namespace);
if (prefix == null) {
return qname;
}
return new QName(qname.getNamespaceURI(),qname.getLocalPart(),prefix);
}
@Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
//for JAXB we are mapping midpoint common namespace to default namespace
if (defaultNamespace != null && defaultNamespace.equals(namespaceUri) && !alwaysExplicit) {
return "";
}
return getPreferredPrefix(namespaceUri, suggestion);
}
/**
*
* @param namespace
* @return preferred prefix for the namespace, if no prefix is assigned yet,
* then it will assign a prefix and return it.
*/
public static synchronized String getPreferredPrefix(String namespace) {
return getPreferredPrefix(namespace, null);
}
/**
* @param namespace
* @param hintPrefix
* @return preferred prefix for the namespace, if no prefix is assigned yet,
* then it assign hint prefix (if it is not assigned yet) or assign
* a new prefix and return it (if hint prefix is already assigned to
* other namespace).
*/
public static synchronized String getPreferredPrefix(String namespace, String hintPrefix) {
String prefix = globalNamespacePrefixMap.get(namespace);
if (StringUtils.isEmpty(prefix)) {
if (StringUtils.isEmpty(hintPrefix)) {
// FIXME: improve new prefix assignment
prefix = "gen" + (new Random()).nextInt(999);
} else {
if (globalNamespacePrefixMap.containsValue(hintPrefix)) {
// FIXME: improve new prefix assignment
prefix = "gen" + (new Random()).nextInt(999);
} else {
prefix = hintPrefix;
}
}
globalNamespacePrefixMap.put(namespace, prefix);
}
return prefix;
}
@Override
public synchronized GlobalDynamicNamespacePrefixMapper clone() {
GlobalDynamicNamespacePrefixMapper clone = new GlobalDynamicNamespacePrefixMapper();
clone.defaultNamespace = this.defaultNamespace;
clone.localNamespacePrefixMap = clonePrefixMap(this.localNamespacePrefixMap);
clone.alwaysExplicit = this.alwaysExplicit;
return clone;
}
private Map<String, String> clonePrefixMap(Map<String, String> map) {
if (map == null) {
return null;
}
Map<String, String> clone = new HashMap<String,String>();
for (Entry<String, String> entry: map.entrySet()) {
clone.put(entry.getKey(), entry.getValue());
}
return clone;
}
@Override
public String debugDump() {
return debugDump(0);
}
@Override
public String debugDump(int indent) {
StringBuilder sb = new StringBuilder();
DebugUtil.indentDebugDump(sb, indent);
sb.append("GlobalDynamicNamespacePrefixMapper(");
sb.append(defaultNamespace);
sb.append("):\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("Global map:\n");
DebugUtil.debugDumpMapMultiLine(sb, globalNamespacePrefixMap, indent + 2);
sb.append("\n");
DebugUtil.indentDebugDump(sb, indent + 1);
sb.append("Local map:\n");
DebugUtil.debugDumpMapMultiLine(sb, localNamespacePrefixMap, indent + 2);
return sb.toString();
}
}