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 }