001: /*
002: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: [See end of file]
004: $Id: PrefixMappingImpl.java,v 1.29 2008/01/15 15:51:21 chris-dollin Exp $
005: */
006:
007: package com.hp.hpl.jena.shared.impl;
008:
009: import com.hp.hpl.jena.rdf.model.impl.Util;
010: import com.hp.hpl.jena.shared.*;
011: import com.hp.hpl.jena.util.CollectionFactory;
012:
013: import java.util.*;
014: import org.apache.xerces.util.XMLChar;
015:
016: /**
017: An implementation of PrefixMapping. The mappings are stored in a pair
018: of hash tables, one per direction. The test for a legal prefix is left to
019: xerces's XMLChar.isValidNCName() predicate.
020:
021: @author kers
022: */
023: public class PrefixMappingImpl implements PrefixMapping {
024: protected Map prefixToURI;
025: protected Map URItoPrefix;
026: protected boolean locked;
027:
028: public PrefixMappingImpl() {
029: prefixToURI = CollectionFactory.createHashedMap();
030: URItoPrefix = CollectionFactory.createHashedMap();
031: }
032:
033: protected void set(String prefix, String uri) {
034: prefixToURI.put(prefix, uri);
035: URItoPrefix.put(uri, prefix);
036: }
037:
038: protected String get(String prefix) {
039: return (String) prefixToURI.get(prefix);
040: }
041:
042: public PrefixMapping lock() {
043: locked = true;
044: return this ;
045: }
046:
047: public PrefixMapping setNsPrefix(String prefix, String uri) {
048: checkUnlocked();
049: checkLegal(prefix);
050: if (!prefix.equals(""))
051: checkProper(uri);
052: if (uri == null)
053: throw new NullPointerException(
054: "null URIs are prohibited as arguments to setNsPrefix");
055: set(prefix, uri);
056: return this ;
057: }
058:
059: public PrefixMapping removeNsPrefix(String prefix) {
060: checkUnlocked();
061: String uri = (String) prefixToURI.remove(prefix);
062: regenerateReverseMapping();
063: return this ;
064: }
065:
066: protected void regenerateReverseMapping() {
067: URItoPrefix.clear();
068: Iterator it = prefixToURI.entrySet().iterator();
069: while (it.hasNext()) {
070: Map.Entry e = (Map.Entry) it.next();
071: URItoPrefix.put(e.getValue(), e.getKey());
072: }
073: }
074:
075: protected void checkUnlocked() {
076: if (locked)
077: throw new JenaLockedException(this );
078: }
079:
080: private void checkProper(String uri) { // suppressed by popular demand. TODO consider optionality
081: // if (!isNiceURI( uri )) throw new NamespaceEndsWithNameCharException( uri );
082: }
083:
084: public static boolean isNiceURI(String uri) {
085: if (uri.equals(""))
086: return false;
087: char last = uri.charAt(uri.length() - 1);
088: return Util.notNameChar(last);
089: }
090:
091: /**
092: Add the bindings of other to our own. We defer to the general case
093: because we have to ensure the URIs are checked.
094:
095: @param other the PrefixMapping whose bindings we are to add to this.
096: */
097: public PrefixMapping setNsPrefixes(PrefixMapping other) {
098: return setNsPrefixes(other.getNsPrefixMap());
099: }
100:
101: /**
102: Answer this PrefixMapping after updating it with the <code>(p, u)</code>
103: mappings in <code>other</code> where neither <code>p</code> nor
104: <code>u</code> appear in this mapping.
105: */
106: public PrefixMapping withDefaultMappings(PrefixMapping other) {
107: checkUnlocked();
108: Iterator it = other.getNsPrefixMap().entrySet().iterator();
109: while (it.hasNext()) {
110: Map.Entry e = (Map.Entry) it.next();
111: String prefix = (String) e.getKey(), uri = (String) e
112: .getValue();
113: if (getNsPrefixURI(prefix) == null
114: && getNsURIPrefix(uri) == null)
115: setNsPrefix(prefix, uri);
116: }
117: return this ;
118: }
119:
120: /**
121: Add the bindings in the prefixToURI to our own. This will fail with a ClassCastException
122: if any key or value is not a String; we make no guarantees about order or
123: completeness if this happens. It will fail with an IllegalPrefixException if
124: any prefix is illegal; similar provisos apply.
125:
126: @param other the Map whose bindings we are to add to this.
127: */
128: public PrefixMapping setNsPrefixes(Map other) {
129: checkUnlocked();
130: Iterator it = other.entrySet().iterator();
131: while (it.hasNext()) {
132: Map.Entry e = (Map.Entry) it.next();
133: setNsPrefix((String) e.getKey(), (String) e.getValue());
134: }
135: return this ;
136: }
137:
138: /**
139: Checks that a prefix is "legal" - it must be a valid XML NCName.
140: */
141: private void checkLegal(String prefix) {
142: if (prefix.length() > 0 && !XMLChar.isValidNCName(prefix))
143: throw new PrefixMapping.IllegalPrefixException(prefix);
144: }
145:
146: public String getNsPrefixURI(String prefix) {
147: return get(prefix);
148: }
149:
150: public Map getNsPrefixMap() {
151: return CollectionFactory.createHashedMap(prefixToURI);
152: }
153:
154: public String getNsURIPrefix(String uri) {
155: return (String) URItoPrefix.get(uri);
156: }
157:
158: /**
159: Expand a prefixed URI. There's an assumption that any URI of the form
160: Head:Tail is subject to mapping if Head is in the prefix mapping. So, if
161: someone takes it into their heads to define eg "http" or "ftp" we have problems.
162: */
163: public String expandPrefix(String prefixed) {
164: int colon = prefixed.indexOf(':');
165: if (colon < 0)
166: return prefixed;
167: else {
168: String uri = get(prefixed.substring(0, colon));
169: return uri == null ? prefixed : uri
170: + prefixed.substring(colon + 1);
171: }
172: }
173:
174: /**
175: Answer a readable (we hope) representation of this prefix mapping.
176: */
177: public String toString() {
178: return "pm:" + prefixToURI;
179: }
180:
181: /**
182: Answer the qname for <code>uri</code> which uses a prefix from this
183: mapping, or null if there isn't one.
184: <p>
185: Relies on <code>splitNamespace</code> to carve uri into namespace and
186: localname components; this ensures that the localname is legal and we just
187: have to (reverse-)lookup the namespace in the prefix table.
188:
189: @see com.hp.hpl.jena.shared.PrefixMapping#qnameFor(java.lang.String)
190: */
191: public String qnameFor(String uri) {
192: int split = Util.splitNamespace(uri);
193: String ns = uri.substring(0, split), local = uri
194: .substring(split);
195: if (local.equals(""))
196: return null;
197: String prefix = (String) URItoPrefix.get(ns);
198: return prefix == null ? null : prefix + ":" + local;
199: }
200:
201: /**
202: Compress the URI using the prefix mapping. This version of the code looks
203: through all the maplets and checks each candidate prefix URI for being a
204: leading substring of the argument URI. There's probably a much more
205: efficient algorithm available, preprocessing the prefix strings into some
206: kind of search table, but for the moment we don't need it.
207: */
208: public String shortForm(String uri) {
209: Map.Entry e = findMapping(uri, true);
210: return e == null ? uri : e.getKey() + ":"
211: + uri.substring(((String) e.getValue()).length());
212: }
213:
214: public boolean samePrefixMappingAs(PrefixMapping other) {
215: return other instanceof PrefixMappingImpl ? equals((PrefixMappingImpl) other)
216: : equalsByMap(other);
217: }
218:
219: protected boolean equals(PrefixMappingImpl other) {
220: return other.sameAs(this );
221: }
222:
223: protected boolean sameAs(PrefixMappingImpl other) {
224: return prefixToURI.equals(other.prefixToURI);
225: }
226:
227: protected final boolean equalsByMap(PrefixMapping other) {
228: return getNsPrefixMap().equals(other.getNsPrefixMap());
229: }
230:
231: /**
232: Answer a prefixToURI entry in which the value is an initial substring of <code>uri</code>.
233: If <code>partial</code> is false, then the value must equal <code>uri</code>.
234:
235: Does a linear search of the entire prefixToURI, so not terribly efficient for large maps.
236:
237: @param uri the value to search for
238: @param true if the match can be any leading substring, false for exact match
239: @return some entry (k, v) such that uri starts with v [equal for partial=false]
240: */
241: private Map.Entry findMapping(String uri, boolean partial) {
242: Iterator it = prefixToURI.entrySet().iterator();
243: while (it.hasNext()) {
244: Map.Entry e = (Map.Entry) it.next();
245: String ss = (String) e.getValue();
246: if (uri.startsWith(ss)
247: && (partial || ss.length() == uri.length()))
248: return e;
249: }
250: return null;
251: }
252:
253: }
254:
255: /*
256: (c) Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
257: All rights reserved.
258:
259: Redistribution and use in source and binary forms, with or without
260: modification, are permitted provided that the following conditions
261: are met:
262:
263: 1. Redistributions of source code must retain the above copyright
264: notice, this list of conditions and the following disclaimer.
265:
266: 2. Redistributions in binary form must reproduce the above copyright
267: notice, this list of conditions and the following disclaimer in the
268: documentation and/or other materials provided with the distribution.
269:
270: 3. The name of the author may not be used to endorse or promote products
271: derived from this software without specific prior written permission.
272:
273: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
274: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
275: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
276: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
277: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
278: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
279: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
280: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
281: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
282: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
283: */
|