001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.subversion.config;
042:
043: import java.io.ByteArrayOutputStream;
044: import java.io.EOFException;
045: import java.io.File;
046: import java.io.IOException;
047: import java.io.InputStream;
048: import java.io.OutputStream;
049: import java.io.UnsupportedEncodingException;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.Map;
053: import java.util.TreeMap;
054: import java.util.logging.Level;
055: import org.netbeans.modules.subversion.Subversion;
056: import org.netbeans.modules.subversion.util.FileUtils;
057:
058: /**
059: * Handles the credential or property files used by Subversion.
060: *
061: * @author Tomas Stupka
062: *
063: */
064: public class KVFile {
065:
066: /** a Map holding the entries*/
067: private Map<Key, byte[]> map;
068: /** a Map holding the keys*/
069: private Map<String, Key> keyMap;
070: /** the credential or property file */
071: private final File file;
072:
073: /**
074: * Creates a new instance
075: *
076: * @parameter file the credential or property file
077: */
078: public KVFile(File file) {
079: this .file = file;
080: try {
081: if (file.exists()) {
082: parse();
083: }
084: } catch (IOException ex) {
085: Subversion.LOG.log(Level.INFO, null, ex);
086: }
087: }
088:
089: /**
090: * Returns the value for the given Key
091: *
092: * @param key
093: * @return the value stored under the given Key
094: */
095: protected byte[] getValue(Key key) {
096: return (byte[]) getMap().get(key);
097: }
098:
099: /**
100: * Returns the value for the given Key as a String
101: *
102: * @param key
103: * @return the value stored under the given Key as a String
104: */
105: protected String getStringValue(Key key) {
106: try {
107: byte[] value = getValue(key);
108:
109: if (value == null) {
110: return null;
111: }
112: return new String(value, "UTF8");
113: } catch (UnsupportedEncodingException ex) {
114: Subversion.LOG.log(Level.SEVERE, null, ex);
115: return null;
116: }
117: }
118:
119: /**
120: * Stores the given value under the given Key
121: *
122: */
123: protected void setValue(Key key, byte[] value) {
124: getMap().put(key, value);
125: }
126:
127: /**
128: * Returns the Map holding the Key and value pairs
129: *
130: * @return map
131: */
132: private Map<Key, byte[]> getMap() {
133: if (map == null) {
134: map = new TreeMap<Key, byte[]>();
135: }
136: return map;
137: }
138:
139: public Map<String, byte[]> getNormalizedMap() {
140: Map<Key, byte[]> keyValue = getMap();
141: Map<String, byte[]> stringValue = new HashMap<String, byte[]>(
142: keyValue.size());
143: Iterator<Map.Entry<Key, byte[]>> it = keyValue.entrySet()
144: .iterator();
145: while (it.hasNext()) {
146: Map.Entry next = it.next();
147: // getKey().toString() == the normalization
148: stringValue.put(next.getKey().toString(), (byte[]) next
149: .getValue());
150: }
151: return stringValue;
152: }
153:
154: /**
155: * Returns the Map holding the Keys
156: *
157: * @return map
158: */
159: private Map<String, Key> getKeyMap() {
160: if (keyMap == null) {
161: keyMap = new HashMap<String, Key>();
162: }
163: return keyMap;
164: }
165:
166: protected Key getKey(Key key) {
167: Key storedKey = getKey(key.getName());
168: if (storedKey == null) {
169: setKey(key);
170: return key;
171: }
172: return storedKey;
173: }
174:
175: private Key getKey(String name) {
176: return getKeyMap().get(name);
177: }
178:
179: protected void setKey(Key key) {
180: getKeyMap().put(key.getName(), key);
181: }
182:
183: /**
184: * Parses the instances file.
185: *
186: */
187: private void parse() throws IOException {
188: InputStream is = null;
189: try {
190: is = FileUtils.createInputStream(file);
191: int keyIdx = 0;
192: while (!checkEOF(is)) {
193: int keyLength = readEntryLength(is); // key length
194: byte[] keyName = new byte[keyLength];
195: is.read(keyName);
196: is.read(); // skip '\n'
197: int valueLength = readEntryLength(is); // value length
198: byte[] value = new byte[valueLength];
199: is.read(value);
200: Key key = new Key(keyIdx, new String(keyName, "UTF8"));
201: setKey(key);
202: getMap().put(key, value);
203: is.read(); // skip '\n'
204: keyIdx++;
205: }
206: } catch (EOFException eofe) {
207: if (getMap().size() > 0) {
208: // there are already some key-value pairs ->
209: // something in the file structure seems to be wrong
210: throw new EOFException(file.getAbsolutePath());
211: }
212: // otherwise skip the exception, could be just an empty file
213: } finally {
214: try {
215: if (is != null) {
216: is.close();
217: }
218: } catch (IOException e) {
219: Subversion.LOG.log(Level.INFO, null, e); // should not happen
220: }
221: }
222: }
223:
224: private boolean checkEOF(InputStream is) throws IOException {
225: is.mark(3);
226: byte[] end = new byte[3];
227: is.read(end);
228: is.reset();
229: if (end[0] == -1 || end[1] == -1 || end[2] == -1) {
230: throw new EOFException();
231: }
232: return end[0] == 'E' && end[1] == 'N' && end[2] == 'D';
233: }
234:
235: private int readEntryLength(InputStream is) throws IOException {
236: ByteArrayOutputStream baos = new ByteArrayOutputStream();
237: byte b = (byte) is.read();
238: while (b != '\n') {
239: if (b == -1) {
240: throw new EOFException();
241: }
242: baos.write(b);
243: b = (byte) is.read();
244: }
245: String line = baos.toString();
246: return Integer.decode(line.substring(2)).intValue();
247: }
248:
249: public void store() throws IOException {
250: store(file);
251: }
252:
253: public void store(File file) throws IOException {
254: OutputStream os = null;
255: try {
256: File parent = file.getParentFile();
257: if (parent != null && !parent.exists()) {
258: parent.mkdirs();
259: }
260: os = FileUtils.createOutputStream(file);
261: for (Iterator it = getMap().keySet().iterator(); it
262: .hasNext();) {
263: Key key = (Key) it.next();
264: byte[] value = (byte[]) getMap().get(key);
265:
266: StringBuffer sb = new StringBuffer();
267: sb.append("K "); // NOI18N
268: sb.append(key.getName().length());
269: sb.append("\n"); // NOI18N
270: sb.append(key.getName());
271: sb.append("\n"); // NOI18N
272: sb.append("V "); // NOI18N
273: sb.append(value.length);
274: sb.append("\n"); // NOI18N
275: os.write(sb.toString().getBytes("UTF8"));
276: os.write(value);
277: os.write("\n".getBytes()); // NOI18N
278: }
279: os.write("END\n".getBytes()); // NOI18N
280: os.flush();
281:
282: } finally {
283: if (os != null) {
284: try {
285: os.close();
286: } catch (IOException ex) {
287: Subversion.LOG.log(Level.INFO, null, ex);
288: }
289: }
290: }
291: }
292:
293: protected File getFile() {
294: return file;
295: }
296:
297: void setValue(Key key, String value) {
298: setValue(key, value.getBytes());
299: }
300:
301: /**
302: * Represents a key
303: */
304: protected static class Key implements Comparable {
305: /** the key index */
306: private final int idx;
307: /** the keys name */
308: private final String name;
309:
310: /** creates a new instance */
311: protected Key(int idx, String name) {
312: this .name = name;
313: this .idx = idx;
314: }
315:
316: public int getIndex() {
317: return idx;
318: }
319:
320: public String getName() {
321: return name;
322: }
323:
324: public boolean equals(Object obj) {
325: if (!(obj instanceof Key)) {
326: return false;
327: }
328: Key key = (Key) obj;
329: return key.getIndex() == getIndex()
330: && key.getName().equals(getName());
331: }
332:
333: public int hashCode() {
334: StringBuffer sb = new StringBuffer();
335: sb.append(getName());
336: sb.append(getIndex());
337: return sb.toString().hashCode();
338: }
339:
340: public int compareTo(Object obj) {
341: if (!(obj instanceof Key)) {
342: return 0;
343: }
344: Key key = (Key) obj;
345: if (key.getIndex() < getIndex()) {
346: return 1;
347: } else if (key.getIndex() > getIndex()) {
348: return -1;
349: }
350: return 0;
351: }
352:
353: public String toString() {
354: return name;
355: }
356: }
357:
358: }
|