View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *  http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.any23.rdf;
19  
20  import org.eclipse.rdf4j.model.IRI;
21  import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
22  
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  import java.util.Set;
29  
30  /**
31   * A mapping from prefixes to namespace IRIs. Supports "volatile mappings",
32   * which will be overwritten without notice when mappings are merged,
33   * while for normal mappings this causes an exception. This allows
34   * combining "hard" mappings (which must be retained or something breaks)
35   * and "soft" mappings (which might be read from input RDF files and
36   * should be retained only if they are not in conflict with the hard ones).
37   *
38   * @author Richard Cyganiak (richard@cyganiak.de)
39   */
40  public class Prefixes {
41  
42      public static Prefixes create1(String prefix, String namespaceIRI) {
43          Prefixeshtml#Prefixes">Prefixes result = new Prefixes();
44          result.add(prefix, namespaceIRI);
45          return result;
46      }
47  
48      public static Prefixes createFromMap(Map<String, String> prefixesToNamespaceIRIs, boolean areVolatile) {
49          Prefixeshtml#Prefixes">Prefixes result = new Prefixes();
50          for (Entry<String, String> entry : prefixesToNamespaceIRIs.entrySet()) {
51              if (areVolatile) {
52                  result.addVolatile(entry.getKey(), entry.getValue());
53              } else {
54                  result.add(entry.getKey(), entry.getValue());
55              }
56          }
57          return result;
58      }
59  
60      public static Prefixes.html#Prefixes">Prefixes EMPTY = new Prefixes(Collections.<String, String>emptyMap());
61  
62      private final Map<String, String> mappings;
63      private final Set<String> volatilePrefixes = new HashSet<String>();
64  
65      public Prefixes() {
66          this(new HashMap<String, String>());
67      }
68  
69      public Prefixes" href="../../../../org/apache/any23/rdf/Prefixes.html#Prefixes">Prefixes(Prefixes initial) {
70          this();
71          add(initial);
72      }
73  
74      private Prefixes(Map<String, String> mappings) {
75          this.mappings = mappings;
76      }
77  
78      public IRI expand(String curie) {
79          String prefix = parsePrefix(curie);
80          if (prefix == null || !hasPrefix(prefix)) {
81              return null;
82          }
83          return SimpleValueFactory.getInstance().createIRI(
84                  getNamespaceIRIFor(prefix) + parseLocalName(curie));
85      }
86  
87      public String abbreviate(String uri) {
88          for (Entry<String, String> namespace : mappings.entrySet()) {
89              if (uri.startsWith(namespace.getValue())) {
90                  return namespace.getKey() + ":" +
91                          uri.substring(namespace.getValue().length());
92              }
93          }
94          return null;
95      }
96  
97      public boolean canExpand(String curie) {
98          String prefix = parsePrefix(curie);
99          return prefix != null && hasPrefix(prefix);
100     }
101 
102     public boolean canAbbreviate(String uri) {
103         for (Entry<String, String> namespace : mappings.entrySet()) {
104             if (uri.startsWith(namespace.getValue())) {
105                 return true;
106             }
107         }
108         return false;
109     }
110 
111     public String getNamespaceIRIFor(String prefix) {
112         return mappings.get(prefix);
113     }
114 
115     public boolean hasNamespaceIRI(String uri) {
116         return mappings.containsValue(uri);
117     }
118 
119     public boolean hasPrefix(String prefix) {
120         return mappings.containsKey(prefix);
121     }
122 
123     public Set<String> allPrefixes() {
124         return mappings.keySet();
125     }
126 
127     public boolean isEmpty() {
128         return mappings.isEmpty();
129     }
130 
131     public void add(String prefix, String namespaceIRI) {
132         if (isVolatile(prefix)) {
133             volatilePrefixes.remove(prefix);
134         } else {
135             if (hasPrefix(prefix)) {
136                 if (getNamespaceIRIFor(prefix).equals(namespaceIRI)) {
137                     return;    // re-assigned same prefix to same IRI, let's just ignore it
138                 }
139                 throw new IllegalStateException("Attempted to re-assign prefix '" + prefix +
140                         "'; clashing values '" + getNamespaceIRIFor(prefix) + "' and '" +
141                         namespaceIRI);
142             }
143         }
144         mappings.put(prefix, namespaceIRI);
145     }
146 
147     public void add(Prefixes other) {
148         for (String otherPrefix : other.allPrefixes()) {
149             if (other.isVolatile(otherPrefix)) {
150                 addVolatile(otherPrefix, other.getNamespaceIRIFor(otherPrefix));
151             } else {
152                 add(otherPrefix, other.getNamespaceIRIFor(otherPrefix));
153             }
154         }
155     }
156 
157     public void removePrefix(String prefix) {
158         mappings.remove(prefix);
159         volatilePrefixes.remove(prefix);
160     }
161 
162     public Prefixes createSubset(String... prefixes) {
163         Prefixeshtml#Prefixes">Prefixes result = new Prefixes();
164         for (String prefix : prefixes) {
165             if (!hasPrefix(prefix)) {
166                 throw new IllegalArgumentException("No namespace IRI declared for prefix " + prefix);
167             }
168             result.add(prefix, getNamespaceIRIFor(prefix));
169         }
170         return result;
171     }
172 
173     public void addVolatile(String prefix, String namespaceIRI) {
174         if (hasPrefix(prefix)) {
175             return;    // new prefix is volatile, so we don't overwrite the old one
176         }
177         mappings.put(prefix, namespaceIRI);
178         volatilePrefixes.add(prefix);
179     }
180 
181     public void addVolatile(Prefixes other) {
182         for (String otherPrefix : other.allPrefixes()) {
183             addVolatile(otherPrefix, other.getNamespaceIRIFor(otherPrefix));
184         }
185     }
186 
187     public boolean isVolatile(String prefix) {
188         return volatilePrefixes.contains(prefix);
189     }
190 
191     private Map<String, String> mapUnmodifiable = null;
192 
193     public Map<String, String> asMap() {
194         // Optimization: Create the unmodifiable map only once, lazily
195         if (mapUnmodifiable == null) {
196             mapUnmodifiable = Collections.unmodifiableMap(mappings);
197         }
198         return mapUnmodifiable;
199     }
200 
201     private String parsePrefix(String curie) {
202         int index = curie.indexOf(':');
203         if (index == -1) {
204             throw new IllegalArgumentException("Not a CURIE: '" + curie + "'");
205         }
206         return curie.substring(0, index);
207     }
208 
209     private String parseLocalName(String curie) {
210         int index = curie.indexOf(':');
211         if (index == -1) {
212             throw new IllegalArgumentException("Not a CURIE: '" + curie + "'");
213         }
214         return curie.substring(index + 1);
215     }
216     
217 }