/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */
package datasource.explorer;

import java.io.File;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

import ilog.cpl.model.AttributeValueEvent;
import ilog.cpl.model.AttributeValueListener;
import ilog.cpl.model.IlpAttribute;
import ilog.cpl.model.IlpAttributeGroup;
import ilog.cpl.model.IlpAttributeValueChangeSupport;
import ilog.cpl.model.IlpAttributeValueHolder;
import ilog.cpl.model.IlpClass;
import ilog.cpl.model.IlpDefaultAttribute;
import ilog.cpl.model.IlpDefaultClass;
import ilog.cpl.model.IlpObject;

/**
 * JTGO data source explorer sample 2 using a custom <code>IlpClass</code>. See
 * <A href="../../../index.html">index.html</A> for a description of this
 * sample.
 *
 * This class implements the <code>IlpObject</code> interface to wrap
 * <code>java.io.File</code>.
 */
public class FileObject implements IlpObject {
  // Create the attributes of the IlpClass
  public static IlpDefaultAttribute EXISTS = new IlpDefaultAttribute("exists", Boolean.class, true);
  public static IlpDefaultAttribute NAME = new IlpDefaultAttribute("name", String.class, true);
  public static IlpDefaultAttribute PARENT = new IlpDefaultAttribute("parent", String.class, true);
  public static IlpDefaultAttribute PATH = new IlpDefaultAttribute("path", String.class, true);
  public static IlpDefaultAttribute DIRECTORY = new IlpDefaultAttribute("directory", Boolean.class, true);
  public static IlpDefaultAttribute HIDDEN = new IlpDefaultAttribute("hidden", Boolean.class, true);
  public static IlpDefaultAttribute LASTMODIFIED = new IlpDefaultAttribute("lastModified", Long.class, true);
  public static IlpDefaultAttribute LENGTH = new IlpDefaultAttribute("length", Long.class, true);
  public static IlpDefaultAttribute ROOT = new IlpDefaultAttribute("root", Boolean.class, true);
  public static IlpDefaultAttribute TYPE = new IlpDefaultAttribute("type", String.class, true);

  protected static IlpDefaultClass ILPCLASS;

  // Initialize the IlpClass that corresponds to the FileObject Java class
  static {
    ILPCLASS = new IlpDefaultClass("datasource.explorer.FileObject") {
      /**
       * This method is called when objects are loaded from XML.
       */
      Override
      public IlpObject newInstance(IlpClass ilpClass, Object identifier, boolean initializeAttributeValues) {
        return new FileObject((File) identifier, ilpClass);
      }
    };
    ILPCLASS.addAttribute(EXISTS);
    ILPCLASS.addAttribute(NAME);
    ILPCLASS.addAttribute(PARENT);
    ILPCLASS.addAttribute(PATH);
    ILPCLASS.addAttribute(DIRECTORY);
    ILPCLASS.addAttribute(HIDDEN);
    ILPCLASS.addAttribute(LASTMODIFIED);
    ILPCLASS.addAttribute(LENGTH);
    ILPCLASS.addAttribute(ROOT);
    ILPCLASS.addAttribute(TYPE);
  }

  // A FileObject wraps a file
  protected File file;
  // A FileObject instanceis by default of the FileObject IlpClass,
  // but it may be of a subclass, so we need to store the ilpClass here.
  protected IlpClass ilpClass;
  // Use an IlpAttributeValueChangeSupport to handle all notification
  // and listeners registration
  protected IlpAttributeValueChangeSupport support = new IlpAttributeValueChangeSupport(this);
  // Use an hashmap to cache some attribute values, as the File class
  // always access the file system each time one its methods is called
  protected Map<IlpAttribute, Object> attributeValueCache = new HashMap<IlpAttribute, Object>();

  /**
   * Creates a <tt>FileObject</tt> from a <tt>java.io.File</tt>
   */
  public FileObject(File file) {
    this.file = file;
    // by default, the IlpClass is the one returned by GetIlpClass()
    this.ilpClass = GetIlpClass();
  }

  /**
   * Creates a <tt>FileObject</tt> from a <tt>java.io.File</tt>. The
   * <tt>IlpClass</tt> parameter permits to support other IlpClasses than the
   * static ILPCLASS defined in the FileObject class.
   */
  public FileObject(File file, IlpClass ilpClass) {
    this.file = file;
    this.ilpClass = ilpClass;
  }

  /***
   * Method GetIlpClass The class managers will recognize this static method and
   * use its result as the <tt>IlpClass</tt> to represent
   * samples.datasource.explorer2.FileObject
   */
  public static IlpClass GetIlpClass() {
    return ILPCLASS;
  }

  // Methods from IlpObject interface

  /**
   * Returns the identifier of the <code>IlpObject</code>. This identifier is an
   * <code>Object</code> that can be used to identify and retrieve this
   * <code>IlpObject</code>. It must be unique.
   */
  Override
  public Object getIdentifier() {
    return file;
  }

  /**
   * Retrieves the IlpClass of this object.
   * 
   * @return The IlpClass of this object.
   */
  Override
  public IlpClass getIlpClass() {
    return ilpClass;
  }

  /**
   * This method initializes the attribute values of the object with the default
   * attribute values, if there are any. There is none here.
   */
  Override
  public void initializeDefaultValues() {
    // nothing to do here
  }

  // Methods from IlpAttributeValueHolder interface

  /**
   * Returns the model that defines which attributes are allowed in this
   * instance.
   */
  Override
  public IlpAttributeGroup getAttributeGroup() {
    return getIlpClass();
  }

  /**
   * Retrieves the value of an attribute of this object.
   * 
   * @param attribute
   *          The attribute whose value is to be retrieved.
   * @return The value of the attribute or <CODE>VALUE_NOT_SET</CODE> if no
   *         value has been set.
   */
  Override
  public Object getAttributeValue(IlpAttribute attribute) {
    if (attributeValueCache.containsKey(attribute)) {
      return attributeValueCache.get(attribute);
    }
    if (attribute == EXISTS) {
      Object result = file.exists() ? Boolean.TRUE : Boolean.FALSE;
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == NAME) {
      String name = file.getName();
      // for the roots the name may be null, so return the path in this case
      Object result = name != null && name.length() != 0 ? name : file.getPath();
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == PARENT) {
      Object result = file.getParent();
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == PATH) {
      Object result = file.getPath();
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == DIRECTORY) {
      Object result = file.isDirectory() ? Boolean.TRUE : Boolean.FALSE;
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == HIDDEN) {
      Object result = file.isHidden() ? Boolean.TRUE : Boolean.FALSE;
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == LASTMODIFIED) {
      Object result = Long.valueOf(file.lastModified());
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == LENGTH) {
      Object result = Long.valueOf(file.length());
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == ROOT) {
      Object result = file.getParent() == null ? Boolean.TRUE : Boolean.FALSE;
      attributeValueCache.put(attribute, result);
      return result;
    }
    if (attribute == TYPE) {
      Object result = URLConnection.getFileNameMap().getContentTypeFor(file.getName());
      attributeValueCache.put(attribute, result);
      return result;
    }
    return null;
  }

  /**
   * Returns <CODE>true</CODE> if the requested attribute is part of the
   * attribute group of this instance and a value has been set for this
   * attribute.
   */
  Override
  public boolean hasAttributeValue(IlpAttribute a) {
    return getAttributeGroup().hasAttribute(a);
  }

  /**
   * Sets the value of an attribute of this object. To remove the value of an
   * attribute, you can pass <CODE>VALUE_NOT_SET</CODE> for the <CODE>value
   * </CODE> argument.
   * 
   * @param attribute
   *          The attribute whose value is set.
   * @param value
   *          The new value of the attribute or <CODE>VALUE_NOT_SET</CODE> to
   *          remove the value of the attribute.
   * @throws IllegalArgumentException
   *           if the attribute cannot have its value modified.
   */
  Override
  public void setAttributeValue(IlpAttribute attribute, Object value) {
    if (attribute == LASTMODIFIED) {
      if (value instanceof Number) {
        file.setLastModified(((Number) value).longValue());
        attributeValueCache.remove(LASTMODIFIED);
        fireEvent(new AttributeValueEvent(this, LASTMODIFIED));
      } else
        throw new IllegalArgumentException(value + " is not acceptable as value of attribute " + attribute);
    }
  }

  /**
   * Retrieves the value of an attribute of this object.
   * 
   * @param attribute
   *          The name of the attribute whose value is to be retrieved.
   * @return The value of the attribute or <CODE>VALUE_NOT_SET</CODE> if no
   *         value has been set.
   */
  Override
  public Object getAttributeValue(String attribute) {
    IlpAttribute attr = getAttributeGroup().getAttribute(attribute);
    if (attr != null)
      return getAttributeValue(attr);
    return IlpAttributeValueHolder.VALUE_NOT_SET;
  }

  /**
   * Sets the value of an attribute of this object. To remove the value of an
   * attribute, you can pass <CODE>VALUE_NOT_SET</CODE> for the <CODE>value
   * </CODE> argument.
   * 
   * @param attribute
   *          The name of the attribute whose value is set.
   * @param value
   *          The new value of the attribute or <CODE>VALUE_NOT_SET</CODE> to
   *          remove the value of the attribute.
   * @throws IllegalArgumentException
   *           if the attribute cannot have its value modified.
   */
  Override
  public void setAttributeValue(String attribute, Object value) {
    IlpAttribute attr = getAttributeGroup().getAttribute(attribute);
    if (attr != null)
      setAttributeValue(attr, value);
  }

  /**
   * Adds a listener to attribute value changes.
   */
  Override
  public void addAttributeValueListener(AttributeValueListener l) {
    support.addAttributeValueListener(l);
  }

  /**
   * Removes the given listener from the attribute value change notifications.
   */
  Override
  public void removeAttributeValueListener(AttributeValueListener l) {
    support.removeAttributeValueListener(l);
  }

  /**
   * Fires an event to the listeners.
   */
  Override
  public void fireEvent(AttributeValueEvent ev) {
    support.fireEvent(ev);
  }
}