Java tutorial
/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program 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 Lesser General Public License for more details. * * Copyright (c) 2002-2017 Hitachi Vantara.. All rights reserved. */ package org.pentaho.reporting.designer.core.settings.prefs; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Properties; import java.util.prefs.AbstractPreferences; import java.util.prefs.BackingStoreException; /** * Todo: Document Me * * @author Thomas Morgner */ public class BinaryPreferences extends AbstractPreferences { private static final Log logger = LogFactory.getLog(BinaryPreferences.class); private Properties properties; private BinaryPreferences parent; private long lastModificationTime; private String rootPath; public BinaryPreferences(final String rootPath) { super(null, ""); this.rootPath = rootPath; } public BinaryPreferences(final BinaryPreferences parent, final String name) { super(parent, name); this.parent = parent; this.properties = new Properties(); } protected void putSpi(final String key, final String value) { initCache(); properties.setProperty(key, value); lastModificationTime = Math.max(lastModificationTime + 1, System.currentTimeMillis()); try { flush(); } catch (BackingStoreException e) { if (logger.isInfoEnabled()) { logger.info("Failed to flush configuration changes");//NON-NLS } else if (logger.isDebugEnabled()) { logger.info("Failed to flush configuration changes", e);//NON-NLS } } } private void initCache() { if (lastModificationTime == 0) { final File pathForNode = new File(getPathForNode()); try { load(pathForNode); } catch (BackingStoreException e) { logger.warn("Failed to load data", e);//NON-NLS // ignored } } } protected String getSpi(final String key) { initCache(); return properties.getProperty(key); } protected void removeSpi(final String key) { initCache(); properties.remove(key); lastModificationTime = Math.max(lastModificationTime + 1, System.currentTimeMillis()); try { flush(); } catch (BackingStoreException e) { if (logger.isInfoEnabled()) { logger.info("Failed to flush configuration changes");//NON-NLS } else if (logger.isDebugEnabled()) { logger.info("Failed to flush configuration changes", e);//NON-NLS } } } public long getLastModificationTime() { return lastModificationTime; } protected String[] keysSpi() throws BackingStoreException { initCache(); return (String[]) properties.keySet().toArray(new String[properties.size()]); } protected String[] childrenNamesSpi() throws BackingStoreException { final ArrayList<String> result = new ArrayList<String>(); final File pathForNode = new File(getPathForNode()); final File[] dirContents = pathForNode.listFiles(); if (dirContents != null) { for (int i = 0; i < dirContents.length; i++) { if (dirContents[i].isDirectory()) { result.add(decodePath(dirContents[i].getName())); } } } return result.toArray(new String[result.size()]); } protected AbstractPreferences childSpi(final String name) { if (name == null || name.length() == 0) { throw new IllegalArgumentException(); } return new BinaryPreferences(this, name); } protected void syncSpi() throws BackingStoreException { final File pathForNode = new File(getPathForNode()); if (pathForNode.exists() == false && properties.isEmpty()) { return; } load(pathForNode); if (pathForNode.exists() == false) { if (pathForNode.mkdirs() == false) { throw new BackingStoreException("Failed to write config " + pathForNode); //$NON-NLS-1$ } } final File target = new File(pathForNode, "prefs.properties");//NON-NLS if (target.exists() == false || target.lastModified() < lastModificationTime) { try { final OutputStream out = new BufferedOutputStream(new FileOutputStream(target)); try { properties.store(out, ""); } finally { out.close(); } } catch (final Exception e) { throw new BackingStoreException("Failed to write config " + target); //$NON-NLS-1$ } } } private void load(final File pathForNode) throws BackingStoreException { if (pathForNode.exists()) { // load .. final File target = new File(pathForNode, "prefs.properties");//NON-NLS if (target.lastModified() > lastModificationTime) { if (target.exists()) { try { final InputStream out = new BufferedInputStream(new FileInputStream(target)); try { properties.clear(); properties.load(out); lastModificationTime = Math.max(lastModificationTime + 1, System.currentTimeMillis()); } finally { out.close(); } } catch (final Exception e) { UncaughtExceptionsModel.getInstance().addException(e); throw new BackingStoreException("Failed to write config " + target); //$NON-NLS-1$ } } } } } protected void flushSpi() throws BackingStoreException { // no-op syncSpi(); } protected void removeNodeSpi() throws BackingStoreException { // delete the directory .. final File pathForNode = new File(getPathForNode()); if (pathForNode.exists()) { final File target = new File(pathForNode, "prefs.properties");//NON-NLS if (target.delete() == false) { throw new BackingStoreException("Unable to delete node-backend"); } if (pathForNode.delete() == false) { throw new BackingStoreException("Unable to delete node-backend"); } } } private String getPathForNode() { if (parent != null) { return parent.getPathForNode() + File.separatorChar + encodePath(name()); } return rootPath; } /** * Encodes the given configuration path. All non-ascii characters get replaced by an escape sequence. * * @param path the path. * @return the translated path. * @throws java.util.prefs.BackingStoreException if something goes wrong. */ private static String decodePath(final String path) throws BackingStoreException { try { final char[] data = path.toCharArray(); final StringBuffer encoded = new StringBuffer(path.length()); int seenDollarIndex = -1; for (int i = 0; i < data.length; i++) { if (seenDollarIndex > -1) { if (data[i] == '$') { encoded.append('$'); seenDollarIndex = -1; continue; } if (i - seenDollarIndex == 4) { final int c = Integer.parseInt(path.substring(seenDollarIndex + 1, i + 1), 16); encoded.append((char) c); seenDollarIndex = -1; continue; } else { continue; } } if (data[i] == '$') { seenDollarIndex = i; } else { encoded.append(data[i]); } } return encoded.toString(); } catch (NumberFormatException nfe) { nfe.printStackTrace(); throw new BackingStoreException("Failed to decode name: " + path); } } /** * Encodes the given configuration path. All non-ascii characters get replaced by an escape sequence. * * @param path the path. * @return the translated path. */ private static String encodePath(final String path) { final char[] data = path.toCharArray(); final StringBuffer encoded = new StringBuffer(path.length()); for (int i = 0; i < data.length; i++) { if (data[i] == '$') { // double quote encoded.append('$'); encoded.append('$'); } else if (Character.isJavaIdentifierPart(data[i]) == false) { // padded hex string encoded.append('$'); final String hex = Integer.toHexString(data[i]); for (int x = hex.length(); x < 4; x++) { encoded.append('0'); } encoded.append(hex); } else { encoded.append(data[i]); } } return encoded.toString(); } }