/*- * Copyright (C) 2008 Erik Larsson * * This program 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. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catacombae.storage.fs; import java.util.HashMap; import java.util.Map; import org.catacombae.storage.io.DataLocator; /** * A factory for creating FileSystemHandlers. By setting attributes in the * factory, you affect how new FileSystemHandlers are created. * * @author <a href="http://www.catacombae.org/" target="_top">Erik Larsson</a> */ public abstract class FileSystemHandlerFactory { /** * This instance holds the create attributes for the current factory. * Subclasses can access this instance directly to query for relevant * attribute definitions and their values. */ protected final Attributes createAttributes; protected FileSystemHandlerFactory() { this.createAttributes = new Attributes(getSupportedStandardAttributes(), getSupportedCustomAttributes()); } /** * Returns the file system recognizer for the current file system type. * * @return the file system recognizer for the current file system type. */ public abstract FileSystemRecognizer getRecognizer(); /** * Creates a new file system handler from the data pointed to by the * supplied DataLocator. * * @param data locator of the file system data. * @return a newly created file system handler. */ public abstract FileSystemHandler createHandler(DataLocator data); public abstract FileSystemHandlerInfo getHandlerInfo(); public abstract FileSystemCapability[] getCapabilities(); public boolean hasCapability(FileSystemCapability c) { for(FileSystemCapability cur : getCapabilities()) { if(cur == c) return true; } return false; } /** * Creates a new instance of this particular factory subclass. * @return a new instance of this particular factory subclass. */ public abstract FileSystemHandlerFactory newInstance(); /** * Returns the attributes that are used to create new file system handlers. * By changing values in this structure, you affect how new handlers are * created. * * @return the attributes that are used to create new file system handlers. */ public Attributes getCreateAttributes() { return createAttributes; } /** * Returns the standard create attributes supported by this implementation. * IMPORTANT: Implementors can set the default values for their supported * standard attributes <b>only in this method</b>. Setting them somewhere * else will be pointless and without effect. * * @return the standard create attributes supported by this implementation. */ public abstract StandardAttribute[] getSupportedStandardAttributes(); /** * Returns the custom create attributes supported by this implementation. * * @return the custom create attributes supported by this implementation. */ public abstract CustomAttribute[] getSupportedCustomAttributes(); /** * Returns the custom attribute by the name <code>name</code>, if supported * by the implementation. Returns <code>null</code> if not supported. * * @param name the name of the requested custom attribute. * @return the custom attribute by the name <code>name</code> if supported, * and <code>null</code> otherwise. */ public CustomAttribute getCustomAttribute(String name) { for(CustomAttribute attr : getSupportedCustomAttributes()) { if(attr.getName().equals(name)) return attr; } return null; } /** * Returns whether or not this standard attribute is supported by the * implementation. * * @param attr the standard attribute to query. * @return whether or not <code>attr</code> is supported. */ public boolean isSupported(StandardAttribute attr) { for(StandardAttribute sa : getSupportedStandardAttributes()) { if(sa == attr) return true; } return false; } /** * Sets the default value of this standard attribute. Factory implementors * can call this method to override the default value for an attribute, in * order to better suit their implementation.<br> * <b>This method must only be called from within * <code>getSupportedStandardAttributes()</code>!</b> * * @param defaultValue the new default value for this standard attribute. */ protected void setStandardAttributeDefaultValue(StandardAttribute attr, Object defaultValue) { attr.setDefaultValue(defaultValue); } /** * Creates a new CustomAttribute, for implementor-defined attributes * that are not general, but specific to the current implementation. * * @param iType the type of the custom attribute. * @param iName the name of the custom attribute. This should typically * be written in C constants-style, like * <code>A_NEW_CUSTOM_ATTRIBUTE</code>, to fit in with the general * aesthetic approach. ;-) * @param iDescription a description of the attribute. * @param iDefaultValue the default newValue of the attribute. * @return a new CustomAttribute object, created from the given parameters. */ protected static CustomAttribute createCustomAttribute(AttributeType iType, String iName, String iDescription, Object iDefaultValue) { //System.err.println("createCustomAttribute(" + iType + ", " + iName + ", " + iDescription + ", " + iDefaultValue + "); invoked"); CustomAttribute createdCustomAttribute = new CustomAttribute(iType, iName, iDescription, iDefaultValue); //System.err.println("Returning a custom attribute: " + createdCustomAttribute); return createdCustomAttribute; } public class Attributes { private final Map<StandardAttribute, Object> standardCreateAttributeMap = new HashMap<StandardAttribute, Object>(); private final Map<CustomAttribute, Object> customCreateAttributeMap = new HashMap<CustomAttribute, Object>(); private Attributes(StandardAttribute[] iSupportedStandardAttributes, CustomAttribute[] iSupportedCustomAttributes) { // Add the supported standard attributes to the list for(StandardAttribute sa : iSupportedStandardAttributes) { standardCreateAttributeMap.put(sa, sa.getDefaultValue()); } // Add the custom attributes to the list for(CustomAttribute ca : iSupportedCustomAttributes) { customCreateAttributeMap.put(ca, ca.getDefaultValue()); } } public final Boolean getBooleanAttribute(StandardAttribute attr) { return getBooleanAttribute(attr.getType(), standardCreateAttributeMap.get(attr)); } public final Long getIntegerAttribute(StandardAttribute attr) { return getIntegerAttribute(attr.getType(), standardCreateAttributeMap.get(attr)); } public final String getStringAttribute(StandardAttribute attr) { return getStringAttribute(attr.getType(), standardCreateAttributeMap.get(attr)); } public final Boolean getBooleanAttribute(CustomAttribute attr) { return getBooleanAttribute(attr.getType(), customCreateAttributeMap.get(attr)); } public final Long getIntegerAttribute(CustomAttribute attr) { return getIntegerAttribute(attr.getType(), customCreateAttributeMap.get(attr)); } public final String getStringAttribute(CustomAttribute attr) { return getStringAttribute(attr.getType(), customCreateAttributeMap.get(attr)); } public void setBooleanAttribute(StandardAttribute attr, Boolean value) { setAttribute(attr, value, standardCreateAttributeMap.get(attr)); } public void setIntegerAttribute(StandardAttribute attr, Integer value) { setAttribute(attr, value, standardCreateAttributeMap.get(attr)); } public void setStringAttribute(StandardAttribute attr, String value) { setAttribute(attr, value, standardCreateAttributeMap.get(attr)); } public void setBooleanAttribute(CustomAttribute attr, Boolean value) { setAttribute(attr, value, customCreateAttributeMap.get(attr)); } public void setIntegerAttribute(CustomAttribute attr, Integer value) { setAttribute(attr, value, customCreateAttributeMap.get(attr)); } public void setStringAttribute(CustomAttribute attr, String value) { setAttribute(attr, value, customCreateAttributeMap.get(attr)); } private final Boolean getBooleanAttribute(AttributeType type, Object value) { if(type != AttributeType.BOOLEAN) { throw new IllegalArgumentException("Tried to get BOOLEAN value " + "from " + type + " type!"); } else if(value == null) { throw new IllegalArgumentException("Attribute is not supported " + "by this implementation!"); } else { if(value instanceof Boolean) { return (Boolean) value; } else { throw new RuntimeException("INTERNAL ERROR: Kick the coder " + "for inserting " + value.getClass() + " values in a " + "BOOLEAN attribute."); } } } private final Long getIntegerAttribute(AttributeType type, Object value) { if(type != AttributeType.INTEGER) { throw new IllegalArgumentException("Tried to get INTEGER value " + "from " + type + " type!"); } else if(value == null) { throw new IllegalArgumentException("Attribute is not supported " + "by this implementation!"); } else { if(value instanceof Long) { return (Long) value; } else if(value instanceof Number) { return ((Number) value).longValue(); } else { throw new RuntimeException("INTERNAL ERROR: Kick the coder " + "for inserting " + value.getClass() + " values in an " + "INTEGER attribute."); } } } private final String getStringAttribute(AttributeType type, Object value) { if(type != AttributeType.STRING) { throw new IllegalArgumentException("Tried to get STRING value " + "from " + type + " type!"); } else if(value == null) { throw new IllegalArgumentException("Attribute is not supported " + "by this implementation!"); } else { if(value instanceof String) { return (String) value; } else { throw new RuntimeException("INTERNAL ERROR: Kick the coder " + "for inserting " + value.getClass() + " values in a " + "STRING attribute."); } } } private void setAttribute(StandardAttribute attr, Object newValue, Object oldValue) { if(!attr.getType().isValidValue(newValue)) { throw new IllegalArgumentException("Invalid value type (" + newValue.getClass() + ") for attribute (" + attr + ")!"); } else if(oldValue == null) { throw new IllegalArgumentException("Attribute " + attr + " is not supported " + "by this implementation!"); } else { standardCreateAttributeMap.put(attr, newValue); } } private void setAttribute(CustomAttribute attr, Object newValue, Object oldValue) { if(!attr.getType().isValidValue(newValue)) { throw new IllegalArgumentException("Invalid value type (" + newValue.getClass() + ") for attribute (" + attr + ")!"); } else if(oldValue == null) { throw new IllegalArgumentException("Attribute " + attr + " is not supported " + "by this implementation!"); } else { customCreateAttributeMap.put(attr, newValue); } } } /** * This enum defines the valid attribute types that can be used to create * new custom attributes or change existing standard or custom attributes. */ public static enum AttributeType { BOOLEAN(Boolean.class), INTEGER(Byte.class, Short.class, Integer.class, Long.class), STRING(String.class); private final Class[] valueSuperClasses; private AttributeType(Class... iValueSuperClasses) { this.valueSuperClasses = iValueSuperClasses; } /** * Returns whether or not the supplied newValue is valid for this type.<br> * Example: For a BOOLEAN AttributeType only a java.lang.Boolean * is a valid newValue type. For an INTEGER AttributeType all the standard * integer-values are valid (java.lang.Byte, java.lang.Short, * java.lang.Integer, java.lang.Long... BigInteger is not supported). * * @param value the value to check for validity. * @return whether or not the supplied value is valid for this type. */ public boolean isValidValue(Object value) { for(Class c : valueSuperClasses) if(c.isInstance(value)) return true; return false; } } /** * This enum defines the standard attributes that are defined for creating * new FileSystemHandlers. They may or may not be supported by the * implementation. You can find out which standard attributes are supported * by the current factory by calling * <code>getSupportedStandardAttributes()</code>. */ public enum StandardAttribute { /** * Boolean attribute which can be applied to file systems with an * internal caching mechanism. Default newValue for this standard attribute * is <code>false</code>. */ CACHING_ENABLED(AttributeType.BOOLEAN, new Boolean(false)); private final AttributeType type; private Object defaultValue; private StandardAttribute(AttributeType iType, Object iDefaultValue) { this.type = iType; setDefaultValue(iDefaultValue); } /** * Returns the type of this standard attribute. * @return the type of this standard attribute. */ public AttributeType getType() { return type; } /** * Returns the default newValue for this standard attribute. * @return the default newValue for this standard attribute. */ public Object getDefaultValue() { return defaultValue; } /** * Sets the default newValue of this standard attribute. Factory * implementors can override this default newValue specific to their * implementation as this enum is instance-bound and not static.<br> * <b>This method must only be called from within * <code>getSupportedStandardAttributes()</code>!</b> * * @param iDefaultValue the new default newValue for this standard * attribute. */ private void setDefaultValue(Object iDefaultValue) { if(!type.isValidValue(iDefaultValue)) throw new IllegalArgumentException("Illegal default value!"); else this.defaultValue = iDefaultValue; } } /** * This class represents a custom create attribute that a file system * implementor may want to be able to specify when creating a * FileSystemHandler. Example: * <pre> * CustomAttribute debugAttribute = new CustomAttribute(AttributeType.BOOLEAN, * "DEBUG_ENABLED", "Controls if debug mode should be used.", false); * CustomAttribute outfileAttribute = new CustomAttribute(AttributeType.STRING, * "DEBUG_OUTPUTFILE", "Controls which outputfile should be used for debug.", * "C:\\Temp\\debug.log"); * </pre> */ public static final class CustomAttribute { private final AttributeType type; private final String name; private final String description; private final Object defaultValue; /** * Creates a new CustomAttribute, for implementor-defined attributes * that are not general, but specific to the current implementation. * * @param iType the type of the custom attribute. * @param iName the name of the custom attribute. This should typically * be written in C constants-style, like * <code>A_NEW_CUSTOM_ATTRIBUTE</code>, to fit in with the general * aesthetic approach. ;-) * @param iDescription a description of the attribute. * @param iDefaultValue the default newValue of the attribute. */ private CustomAttribute(AttributeType iType, String iName, String iDescription, Object iDefaultValue) { // Input check... if(iType == null) throw new IllegalArgumentException("An attribute must have a type."); if(iName == null) throw new IllegalArgumentException("An attribute must have a name."); if(iDescription == null) throw new IllegalArgumentException("An attribute must have a description."); if(iDefaultValue == null) throw new IllegalArgumentException("An attribute must have a default value."); else if(!iType.isValidValue(iDefaultValue)) throw new IllegalArgumentException("Illegal default value!"); this.type = iType; this.name = iName; this.description = iDescription; this.defaultValue = iDefaultValue; } /** * Returns the type of this custom attribute. * * @return the type of this custom attribute. */ public AttributeType getType() { return type; } /** * Returns the name of this custom attribute. * * @return the name of this custom attribute. */ public String getName() { return name; } /** * Returns a brief description of this custom attribute. * * @return a brief description of this custom attribute. */ public String getDescription() { return description; } /** * Returns the default newValue for this custom attribute. * * @return the default newValue for this custom attribute. */ public Object getDefaultValue() { return defaultValue; } /** * Sets the default newValue of this custom attribute. Factory * implementors can ovveride this default newValue specific to their * implementation as this enum is instance-bound and not static. * * @param iDefaultValue the new default newValue for this custom * attribute. */ /* // Not needed here protected void setDefaultValue(Object iDefaultValue) { if(!type.isValidValue(iDefaultValue)) throw new IllegalArgumentException("Illegal default newValue!"); else this.defaultValue = iDefaultValue; } * */ } }