001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2006.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.sail.nativerdf;
007:
008: import java.io.DataInputStream;
009: import java.io.DataOutputStream;
010: import java.io.EOFException;
011: import java.io.File;
012: import java.io.FileInputStream;
013: import java.io.FileOutputStream;
014: import java.io.IOException;
015: import java.util.Arrays;
016: import java.util.Iterator;
017: import java.util.LinkedHashMap;
018: import java.util.Map;
019:
020: import info.aduna.io.IOUtil;
021:
022: import org.openrdf.model.Namespace;
023: import org.openrdf.model.impl.NamespaceImpl;
024:
025: /**
026: * An in-memory store for namespace prefix information that uses a file for
027: * persistence. Namespaces are encoded in the file as records as follows:
028: *
029: * <pre>
030: * byte 1 - 2 : the length of the encoded namespace name
031: * byte 3 - A : the UTF-8 encoded namespace name
032: * byte A+1 - A+2 : the length of the encoded namespace prefix
033: * byte A+3 - end : the UTF-8 encoded namespace prefix
034: * </pre>
035: *
036: * @author Arjohn Kampman
037: */
038: class NamespaceStore implements Iterable<NamespaceImpl> {
039:
040: /*-----------*
041: * Constants *
042: *-----------*/
043:
044: private static final String FILE_NAME = "namespaces.dat";
045:
046: /**
047: * Magic number "Native Namespace File" to detect whether the file is
048: * actually a namespace file. The first three bytes of the file should be
049: * equal to this magic number.
050: */
051: private static final byte[] MAGIC_NUMBER = new byte[] { 'n', 'n',
052: 'f' };
053:
054: /**
055: * File format version, stored as the fourth byte in namespace files.
056: */
057: private static final byte FILE_FORMAT_VERSION = 1;
058:
059: /*-----------*
060: * Variables *
061: *-----------*/
062:
063: /**
064: * The data file for this NamespaceStore.
065: */
066: private File file;
067:
068: /**
069: * Map storing namespace information by their prefix.
070: */
071: private Map<String, NamespaceImpl> namespacesMap;
072:
073: /**
074: * Flag indicating whether the contents of this NamespaceStore are different
075: * from what is stored on disk.
076: */
077: private boolean contentsChanged;
078:
079: /*--------------*
080: * Constructors *
081: *--------------*/
082:
083: public NamespaceStore(File dataDir) throws IOException {
084: file = new File(dataDir, FILE_NAME);
085:
086: namespacesMap = new LinkedHashMap<String, NamespaceImpl>(16);
087:
088: if (file.exists()) {
089: readNamespacesFromFile();
090: } else {
091: // Make sure the file exists
092: writeNamespacesToFile();
093: }
094:
095: contentsChanged = false;
096: }
097:
098: /*---------*
099: * Methods *
100: *---------*/
101:
102: public String getNamespace(String prefix) {
103: String result = null;
104: NamespaceImpl namespace = namespacesMap.get(prefix);
105: if (namespace != null) {
106: result = namespace.getName();
107: }
108: return result;
109: }
110:
111: public void setNamespace(String prefix, String name) {
112: NamespaceImpl ns = namespacesMap.get(prefix);
113:
114: if (ns != null) {
115: if (!ns.getName().equals(name)) {
116: ns.setName(name);
117: contentsChanged = true;
118: }
119: } else {
120: namespacesMap.put(prefix, new NamespaceImpl(prefix, name));
121: contentsChanged = true;
122: }
123: }
124:
125: public void removeNamespace(String prefix) {
126: NamespaceImpl ns = namespacesMap.remove(prefix);
127:
128: if (ns != null) {
129: contentsChanged = true;
130: }
131: }
132:
133: public Iterator<NamespaceImpl> iterator() {
134: return namespacesMap.values().iterator();
135: }
136:
137: public void clear() {
138: if (!namespacesMap.isEmpty()) {
139: namespacesMap.clear();
140: contentsChanged = true;
141: }
142: }
143:
144: public void sync() throws IOException {
145: if (contentsChanged) {
146: // Flush the changes to disk
147: writeNamespacesToFile();
148: contentsChanged = false;
149: }
150: }
151:
152: public void close() {
153: namespacesMap = null;
154: file = null;
155: }
156:
157: /*----------*
158: * File I/O *
159: *----------*/
160:
161: private void writeNamespacesToFile() throws IOException {
162: synchronized (file) {
163: DataOutputStream out = new DataOutputStream(
164: new FileOutputStream(file));
165:
166: try {
167: out.write(MAGIC_NUMBER);
168: out.writeByte(FILE_FORMAT_VERSION);
169:
170: for (Namespace ns : namespacesMap.values()) {
171: out.writeUTF(ns.getName());
172: out.writeUTF(ns.getPrefix());
173: }
174: } finally {
175: out.close();
176: }
177: }
178: }
179:
180: private void readNamespacesFromFile() throws IOException {
181: synchronized (file) {
182: DataInputStream in = new DataInputStream(
183: new FileInputStream(file));
184:
185: try {
186: byte[] magicNumber = IOUtil.readBytes(in,
187: MAGIC_NUMBER.length);
188: if (!Arrays.equals(magicNumber, MAGIC_NUMBER)) {
189: throw new IOException(
190: "File doesn't contain compatible namespace data");
191: }
192:
193: byte version = in.readByte();
194: if (version > FILE_FORMAT_VERSION) {
195: throw new IOException(
196: "Unable to read namespace file; it uses a newer file format");
197: } else if (version != FILE_FORMAT_VERSION) {
198: throw new IOException(
199: "Unable to read namespace file; invalid file format version: "
200: + version);
201: }
202:
203: while (true) {
204: try {
205: String name = in.readUTF();
206: String prefix = in.readUTF();
207:
208: NamespaceImpl ns = new NamespaceImpl(prefix,
209: name);
210: namespacesMap.put(name, ns);
211: } catch (EOFException e) {
212: break;
213: }
214: }
215: } finally {
216: in.close();
217: }
218: }
219: }
220:
221: /*-------------------*
222: * Debugging methods *
223: *-------------------*/
224:
225: public static void main(String[] args) throws Exception {
226: NamespaceStore nsStore = new NamespaceStore(new File(args[0]));
227:
228: for (Namespace ns : nsStore) {
229: System.out.println(ns.getPrefix() + " = " + ns.getName());
230: }
231: }
232: }
|