/** * PropertyFileReader.java * @author NJ Pearman * @date 3 Oct 2008 * * This program is distributed under the terms of the GNU General Public * License * Copyright 2008 NJ Pearman * * This file is part of MobScrob. * * MobScrob is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MobScrob is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MobScrob. If not, see <http://www.gnu.org/licenses/>. */ package mobscrob.properties; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import java.util.Vector; import javax.microedition.io.Connector; import javax.microedition.io.file.FileConnection; import mobscrob.logging.Log; import mobscrob.logging.LogFactory; import mobscrob.util.StreamUtil; /** * @author Neill * */ public class PropertyFilePersistor implements PropertyPersistor { private static final Log log = LogFactory.getLogger(PropertyFilePersistor.class); private static final String PROPERTIES_FILENAME = "file:///e:/mobscrob.properties"; protected static final char CHAR_EQUALS = '='; protected static final char CHAR_CARRIAGE_RETURN = '\r'; private boolean readKey; private StringBuffer key, value; private Vector keyValues; private boolean readingComment; private boolean deleteFileOnLoad = true; public PropertyFilePersistor() { key = new StringBuffer(); value = new StringBuffer(); keyValues = new Vector(); readingComment = false; readKey = false; } private FileConnection openPropertiesFile() throws IOException { FileConnection fc = (FileConnection) Connector.open(PROPERTIES_FILENAME); if (!fc.exists()) { // create Properties fc.create(); } return fc; } /* (non-Javadoc) * @see mobscrob.properties.PropertyReader#load(mobscrob.properties.MobScrobProperties) */ public boolean load(MobScrobProperties properties) { final String methodName = "1"; FileConnection fc = null; InputStream is = null; try { fc = openPropertiesFile(); is = fc.openInputStream(); Vector lines = readKeyValues(is); Enumeration e = lines.elements(); while (e.hasMoreElements()) { KeyValuePair kv = (KeyValuePair) e.nextElement(); if (KEY_USERNAME.equals(kv.key)) { properties.setUsername(kv.value); log.info(methodName, "Loaded username: " + kv.value); } else if (KEY_HASHED_PASSWORD.equals(kv.key)) { properties.setHashedPassword(kv.value); log.info(methodName, "Loaded hashed password: " + kv.value); } else if (KEY_SCROBBLE_OFFLINE.equals(kv.value)) { properties.setScrobbleOffline("true".equals(kv.value.toLowerCase())); log.info(methodName, "Loaded export queue: " + kv.value); } else { log.warn(methodName, "Unexpected property: " + kv.key + ": " + kv.value); } } if(deleteFileOnLoad) { log.info(methodName, "Deleting properties file"); fc.delete(); } return true; } catch (IOException e) { log.error(methodName, "Unable to load properties: " + e.getMessage(), e); return false; } finally { StreamUtil.closeInputStream(is); if (fc != null) { try { fc.close(); } catch (Exception e) { } } } } /* (non-Javadoc) * @see mobscrob.properties.PropertyReader#save(mobscrob.properties.MobScrobProperties) */ public void save(MobScrobProperties properties) { final String methodName = "2"; FileConnection fc = null; OutputStream os = null; try { log.info(methodName, "Saving properties..."); fc = openPropertiesFile(); int bytesWritten = 0; // write output to stream os = fc.openOutputStream(); // write username bytesWritten += writePropertyToStream(os, KEY_USERNAME.getBytes(), properties.getUsername().getBytes()); // write password bytesWritten += writePropertyToStream(os, KEY_HASHED_PASSWORD.getBytes(), properties.getHashedPassword().getBytes()); bytesWritten += writePropertyToStream(os, KEY_SCROBBLE_OFFLINE.getBytes(), String.valueOf(properties.scrobbleOffline()).getBytes()); // truncate to length of written bytes fc.truncate(bytesWritten); log.info(methodName, "Saved properties " + this.toString()); } catch (IOException e) { log.error(methodName, "Unable to write properties file: " + e.getMessage(), e); } finally { if (os != null) { try { os.flush(); os.close(); } catch (Exception e) { log.error(methodName, "Unable to close output stream: " + e.getMessage(), e); } } if (fc != null) { try { fc.close(); } catch (Exception e) {} } } } /** * Writes the given property to the specified output stream. Returns the * total number of bytes written to the output stream by this call * * @param os * @param key * @param value * @return * @throws IOException */ protected int writePropertyToStream(OutputStream os, byte[] key, byte[] value) throws IOException { int totalBytes = key.length + value.length + 2; os.write(key); os.write(CHAR_EQUALS); os.write(value); os.write(CHAR_CARRIAGE_RETURN); return totalBytes; } protected Vector readKeyValues(InputStream is) throws IOException { final String methodName = "3"; int next, big, little; KeyValuePair kv; PropertyFilePersistor reader = new PropertyFilePersistor(); // read first two bytes to see if 16bit int first = is.read(); int second = is.read(); if (first == 0xFE && second == 0xFF) { // big endian 16 bit log.info(methodName, "Reading 16 bit big endian"); while (((big = is.read()) > -1) && ((little = is.read()) > -1)) { next = 0; next |= big << 8; next |= little; interpretByte(next, reader); } } else if (first == 0xFF && second == 0xFE) { // little endian 16 bit log.info(methodName, "Reading 16 bit little endian"); while (((big = is.read()) > -1) && ((little = is.read()) > -1)) { next = 0; next |= big; next |= little << 8; interpretByte(next, reader); } } else { log.info(methodName, "Reading 8 bit"); // first interpret the first two bytes interpretByte(first, reader); interpretByte(second, reader); while ((next = is.read()) > -1) { // read the rest of the stream interpretByte(next, reader); } } if (reader.key.length() > 0) { kv = new KeyValuePair(); kv.key = reader.key.toString(); kv.value = reader.value.toString(); reader.keyValues.addElement(kv); } return reader.keyValues; } private void interpretByte(int nextByte, PropertyFilePersistor reader) { if (nextByte == '\r' || nextByte == '\n') { reader.readingComment = false; // save key-value pair if key not empty if (reader.key.length() > 0) { KeyValuePair kv = new KeyValuePair(); kv.key = reader.key.toString(); kv.value = reader.value.toString(); reader.keyValues.addElement(kv); } reader.key = new StringBuffer(); reader.value = new StringBuffer(); reader.readKey = false; } else if ((reader.readingComment)) { // skip } else if (nextByte == '#') { // save and clear key value keys reader.readingComment = true; } else if (nextByte == '=') { // finished reading this key reader.readKey = true; } else { /** * @TODO restrict characters to a-zA-Z0-9 plus some special chars as * defined by last.fm username restrictions, plus any others * required */ // append to key / value if (reader.readKey) { reader.value.append((char) nextByte); } else { reader.key.append((char) nextByte); } } } protected class KeyValuePair { protected String key; protected String value; } }