001 /* 002 * JBoss, Home of Professional Open Source. 003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors 004 * as indicated by the @author tags. See the copyright.txt file in the 005 * distribution for a full listing of individual contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 package org.jboss.dna.graph.properties.basic; 023 024 import java.text.DecimalFormat; 025 import java.util.Collections; 026 import java.util.HashMap; 027 import java.util.HashSet; 028 import java.util.Map; 029 import java.util.Set; 030 import java.util.concurrent.locks.Lock; 031 import java.util.concurrent.locks.ReadWriteLock; 032 import java.util.concurrent.locks.ReentrantReadWriteLock; 033 import net.jcip.annotations.GuardedBy; 034 import net.jcip.annotations.ThreadSafe; 035 import org.jboss.dna.common.util.CheckArg; 036 import org.jboss.dna.graph.properties.NamespaceRegistry; 037 038 /** 039 * @author Randall Hauch 040 */ 041 @ThreadSafe 042 public class BasicNamespaceRegistry implements NamespaceRegistry { 043 044 public static final String DEFAULT_NAMESPACE_URI = ""; 045 public static final String DEFAULT_PREFIX_TEMPLATE = "ns##000"; 046 public static final String DEFAULT_PREFIX_NUMBER_FORMAT = "##000"; 047 048 private final ReadWriteLock registryLock = new ReentrantReadWriteLock(); 049 private final Map<String, String> namespacesByPrefix = new HashMap<String, String>(); 050 private final Map<String, String> prefixesByNamespace = new HashMap<String, String>(); 051 private String generatedPrefixTemplate = DEFAULT_PREFIX_TEMPLATE; 052 private int nextGeneratedPrefixNumber = 1; 053 054 /** 055 * 056 */ 057 public BasicNamespaceRegistry() { 058 this(DEFAULT_NAMESPACE_URI); 059 } 060 061 /** 062 * @param defaultNamespaceUri the namespace URI to use for the default prefix 063 */ 064 public BasicNamespaceRegistry( final String defaultNamespaceUri ) { 065 register("", defaultNamespaceUri); 066 } 067 068 /** 069 * @return prefixTemplate 070 */ 071 public String getGeneratedPrefixTemplate() { 072 Lock lock = this.registryLock.readLock(); 073 try { 074 lock.lock(); 075 return this.generatedPrefixTemplate; 076 } finally { 077 lock.unlock(); 078 } 079 } 080 081 /** 082 * @param prefixTemplate Sets prefixTemplate to the specified value. 083 */ 084 public void setGeneratedPrefixTemplate( String prefixTemplate ) { 085 if (prefixTemplate == null) prefixTemplate = DEFAULT_PREFIX_TEMPLATE; 086 Lock lock = this.registryLock.writeLock(); 087 try { 088 lock.lock(); 089 this.generatedPrefixTemplate = prefixTemplate; 090 } finally { 091 lock.unlock(); 092 } 093 } 094 095 /** 096 * {@inheritDoc} 097 */ 098 public String getNamespaceForPrefix( String prefix ) { 099 CheckArg.isNotNull(prefix, "prefix"); 100 Lock lock = this.registryLock.readLock(); 101 try { 102 lock.lock(); 103 return this.namespacesByPrefix.get(prefix); 104 } finally { 105 lock.unlock(); 106 } 107 } 108 109 /** 110 * {@inheritDoc} 111 */ 112 public String getPrefixForNamespaceUri( String namespaceUri, 113 boolean generateIfMissing ) { 114 CheckArg.isNotNull(namespaceUri, "namespaceUri"); 115 String prefix = null; 116 Lock lock = this.registryLock.readLock(); 117 try { 118 lock.lock(); 119 prefix = this.prefixesByNamespace.get(namespaceUri); 120 } finally { 121 lock.unlock(); 122 } 123 if (prefix == null && generateIfMissing) { 124 // Get a write lock ... 125 lock = this.registryLock.writeLock(); 126 try { 127 lock.lock(); 128 // Since we got a new lock, we need to check again ... 129 prefix = this.prefixesByNamespace.get(namespaceUri); 130 if (prefix == null) { 131 // Now we can genereate a prefix and register it ... 132 prefix = this.generatePrefix(); 133 this.register(prefix, namespaceUri); 134 } 135 return prefix; 136 } finally { 137 lock.unlock(); 138 } 139 } 140 return prefix; 141 } 142 143 /** 144 * {@inheritDoc} 145 */ 146 public boolean isRegisteredNamespaceUri( String namespaceUri ) { 147 CheckArg.isNotNull(namespaceUri, "namespaceUri"); 148 Lock lock = this.registryLock.readLock(); 149 try { 150 lock.lock(); 151 return this.prefixesByNamespace.containsKey(namespaceUri); 152 } finally { 153 lock.unlock(); 154 } 155 } 156 157 /** 158 * {@inheritDoc} 159 */ 160 public String getDefaultNamespaceUri() { 161 Lock lock = this.registryLock.readLock(); 162 try { 163 lock.lock(); 164 return this.namespacesByPrefix.get(""); 165 } finally { 166 lock.unlock(); 167 } 168 } 169 170 /** 171 * {@inheritDoc} 172 */ 173 public String register( String prefix, 174 String namespaceUri ) { 175 CheckArg.isNotNull(namespaceUri, "namespaceUri"); 176 String previousNamespaceForPrefix = null; 177 namespaceUri = namespaceUri.trim(); 178 Lock lock = this.registryLock.writeLock(); 179 try { 180 lock.lock(); 181 if (prefix == null) prefix = generatePrefix(); 182 prefix = prefix.trim(); 183 prefix = prefix.replaceFirst("^:+", ""); 184 prefix = prefix.replaceFirst(":+$", ""); 185 previousNamespaceForPrefix = this.namespacesByPrefix.put(prefix, namespaceUri); 186 String previousPrefix = this.prefixesByNamespace.put(namespaceUri, prefix); 187 if (previousPrefix != null && !previousPrefix.equals(prefix)) { 188 this.namespacesByPrefix.remove(previousPrefix); 189 } 190 if (previousNamespaceForPrefix != null && !previousNamespaceForPrefix.equals(namespaceUri)) { 191 this.prefixesByNamespace.remove(previousNamespaceForPrefix); 192 } 193 } finally { 194 lock.unlock(); 195 } 196 return previousNamespaceForPrefix; 197 } 198 199 /** 200 * {@inheritDoc} 201 * 202 * @see org.jboss.dna.graph.properties.NamespaceRegistry#unregister(java.lang.String) 203 */ 204 public boolean unregister( String namespaceUri ) { 205 CheckArg.isNotNull(namespaceUri, "namespaceUri"); 206 namespaceUri = namespaceUri.trim(); 207 Lock lock = this.registryLock.writeLock(); 208 try { 209 lock.lock(); 210 String prefix = this.prefixesByNamespace.remove(namespaceUri); 211 if (prefix == null) return false; 212 this.namespacesByPrefix.remove(prefix); 213 } finally { 214 lock.unlock(); 215 } 216 return true; 217 } 218 219 /** 220 * {@inheritDoc} 221 */ 222 public Set<String> getRegisteredNamespaceUris() { 223 Set<String> result = new HashSet<String>(); 224 Lock lock = this.registryLock.readLock(); 225 try { 226 lock.lock(); 227 result.addAll(this.prefixesByNamespace.keySet()); 228 } finally { 229 lock.unlock(); 230 } 231 return Collections.unmodifiableSet(result); 232 } 233 234 /** 235 * {@inheritDoc} 236 * 237 * @see org.jboss.dna.graph.properties.NamespaceRegistry#getNamespaces() 238 */ 239 public Set<Namespace> getNamespaces() { 240 Set<Namespace> result = new HashSet<Namespace>(); 241 Lock lock = this.registryLock.readLock(); 242 try { 243 lock.lock(); 244 for (Map.Entry<String, String> entry : this.namespacesByPrefix.entrySet()) { 245 result.add(new BasicNamespace(entry.getKey(), entry.getValue())); 246 } 247 } finally { 248 lock.unlock(); 249 } 250 return Collections.unmodifiableSet(result); 251 } 252 253 @GuardedBy( "registryLock" ) 254 protected String generatePrefix() { 255 DecimalFormat formatter = new DecimalFormat(this.generatedPrefixTemplate); 256 return formatter.format(nextGeneratedPrefixNumber++); 257 } 258 259 }