/*
 * 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.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import ilog.cpl.datasource.IlpDefaultDataSource;
import ilog.cpl.datasource.structure.IlpChild;
import ilog.cpl.datasource.structure.IlpContainer;
import ilog.cpl.model.IlpObject;

/**
 * JTGO data source explorer sample 2 using a custom IlpClass. See
 * <A href="../../../index.html">index.html</A> for a description of this
 * sample.
 */
public class FileDataSource extends IlpDefaultDataSource {
  Logger log;

  /**
   * Constructor.
   */
  public FileDataSource(Logger log) {
    // call the constructor of IlpDefaultDataSource
    super();
    this.log = log;
    // loads the roots into the data source
    File[] rootdirs = File.listRoots();
    addFiles(rootdirs);
  }

  /**
   * Adds files to the data source.
   * 
   * @param files
   *          The java.io.Files to be wrapped by FileObject.
   */
  protected synchronized void addFiles(File[] files) {
    // Here we avoid calling IlpDefaultDataSource.addObject, as in this case
    // object would be inserted one by one. It would result in very low
    // performance, as each inserted object will result in one notification,
    // and each notification would result in a redrawing (in the tree for
    // example). So here the IlpObjects are stored in a list before calling
    // the addObjects method which will result in only one notification
    List<IlpObject> filesAsIlpObjects = new ArrayList<IlpObject>(files.length);
    for (int i = 0; i < files.length; i++) {
      // use the file itself as the object identifier
      File id = files[i];
      try {
        if (id.canRead()) {
          if (getObject(id) == null) {
            IlpObject ilpObject = new FileObject(files[i]);
            filesAsIlpObjects.add(ilpObject);
          }
        }
      } catch (Exception e) {
        // just ignore entry
      }
    }
    if (filesAsIlpObjects.size() != 0)
      addObjects(filesAsIlpObjects);
  }

  /**
   * This method in conjunction with the two previous classes realizes
   * load-on-demand in the data source.
   */
  Override
  public synchronized IlpContainer getContainerInterface(Object idOrIlpObject) {
    // retrieve the object from the id or the object
    IlpObject object = idOrIlpObject instanceof IlpObject ? (IlpObject) idOrIlpObject : getObject(idOrIlpObject);
    // if we retrieved the object and it is an instance of
    // FileObject.GetIlpClass()
    if (object != null && FileObject.GetIlpClass().isAssignableFrom(object.getIlpClass())) {
      // retrieve the file, and test if it is a directory, if not, return null.
      Boolean isDirectory = (Boolean) object.getAttributeValue(FileObject.DIRECTORY);
      if (isDirectory.booleanValue())
        return DIRECTORY_LOADER;
      else
        return null;
    }
    // for all other classes than FileObject.GetIlpClass(), use the regular
    // implementation,
    // So the FileDataSource can behave like an ordinary IlpDefaultDataSource
    // for
    // all other classes.
    return super.getContainerInterface(idOrIlpObject);
  }

  /**
   * Container interface for file objects. This interface uses the listFiles
   * method to get the list of children. It tries to add them to the data source
   * content, doing the loading on demand, and then returns the list of
   * identifiers.
   */
  IlpContainer DIRECTORY_LOADER = new IlpContainer() {
    Override
    public Collection<Object> getChildren(IlpObject object) {
      // the file is supposed to be a directory
      File directory = (File) object.getIdentifier();
      File[] files = directory.listFiles();
      if (files == null)
        return Collections.emptyList();
      List<Object> filesIds = new ArrayList<Object>(files.length);
      for (int i = 0; i < files.length; i++) {
        // 1st get all the identifiers
        filesIds.add(files[i]);
      }

      // then add the files
      addFiles(files);
      return filesIds;
    }
  };

  /**
   * This method in conjunction with the two previous classes realizes
   * load-on-demand in the data source.
   */
  Override
  public synchronized IlpChild getChildInterface(Object idOrIlpObject) {
    // retrieve the object from the id or the object
    IlpObject object = idOrIlpObject instanceof IlpObject ? (IlpObject) idOrIlpObject : getObject(idOrIlpObject);
    // if the object is an instance of FileObject.GetIlpClass()
    if (object != null && FileObject.GetIlpClass().isAssignableFrom(object.getIlpClass())) {
      // retrieve the file it contains and returns it (see FILE_PARENT below).
      File file = (File) object.getIdentifier();
      File parentFile = file.getParentFile();
      if (parentFile != null) {
        return FILE_PARENT;
      } else {
        return null;
      }
    }
    return super.getChildInterface(idOrIlpObject);
  }

  /**
   * Child interface for file objects. This interface uses the getParentFile
   * method to get the parent.
   */
  IlpChild FILE_PARENT = new IlpChild() {
    Override
    public Object getParent(IlpObject object) {
      File file = (File) object.getIdentifier();
      File parentFile = file.getParentFile();
      return parentFile;
    }
  };
}