/*
 * 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 ilog.cpl.datasource.IlpDefaultDataSource;
import ilog.cpl.datasource.structure.IlpChild;
import ilog.cpl.datasource.structure.IlpContainer;
import ilog.cpl.model.IlpBeansObject;
import ilog.cpl.model.IlpClass;
import ilog.cpl.model.IlpDefaultClass;
import ilog.cpl.model.IlpMutableClass;
import ilog.cpl.model.IlpMutableClassManager;
import ilog.cpl.model.IlpObject;


/**
 * JTGO data source explorer sample.
 * See <A href="../../../index.html">index.html</A> for a description of this sample.
 */
public class FileDataSource extends IlpDefaultDataSource {
    /**
     * The IlpBeansClass instance that represents java.io.File
     */
    IlpMutableClass fileClass;
    /**
     * The IlpClass instance that represents the roots
     */
    IlpClass classOfRoots;

    /**
     * Constructor.
     */
    public FileDataSource() {
      // call the constructor of IltDefaultDataSource
      super();
      // Retrieve the File Java class as an IlpClass
      // By doing it this way, the java.io.File class is being considered as
      // a JavaBean, that is the class manager automatically create a
      // corresponding IlpBeansClass instance in which only the get methods
      // of java.io.File are translated as attributes.
      IlpMutableClassManager classManager = getContext().getClassManager();
      fileClass = (IlpMutableClass)classManager.getClass(File.class.getName());
      // Creates a sub class of the java.io.File IlpClass for root objects,
      // so it's possible to apply a different style on them.
      classOfRoots = new IlpDefaultClass("DirectoryRoot",fileClass,Collections.<IlpClass>emptyList());
      fileClass.addSubClass(classOfRoots);
      classManager.addClass(classOfRoots);
      // loads the roots into the data source
      File[] rootdirs = File.listRoots();
      addFiles(rootdirs, classOfRoots);
    }

    /**
     * Adds files to the data source.
     * @param files The java.io.Files that to be wrapped by IlpBeansObject.
     * @param ilpClass The IlpClass of the wrapping IlpBeansObject.
     */
    protected synchronized void addFiles(File[] files, IlpClass ilpClass) {
      // Here we avoid calling IlpDefaultDataSource.addBean, 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 IlpBeansObject are created explicitely and
      // 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) {
                IlpBeansObject ilpObject = new IlpBeansObject(id,ilpClass,id);
                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 fileClass
      if (object != null && fileClass.isAssignableFrom(object.getIlpClass()))  {
        // retrieve the file, and test if it is a directory, if not, return null.
        File file = (File)((IlpBeansObject)object).getBean();
        if (file.isDirectory())
          return DIRECTORY_LOADER;
        else
          return null;
      }
      // for all other classes than fileObject, use the regular implementation,
      // So the FileDataSource can behave like an ordinary IltDefaultDataSource 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)((IlpBeansObject)object).getBean();
        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, fileClass);
        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 fileClass
      if (object != null && fileClass.isAssignableFrom(object.getIlpClass()))  {
        // retrieve the file it contains and returns it (see FILE_PARENT below).
        File file = (File)((IlpBeansObject)object).getBean();
        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)((IlpBeansObject)object).getBean();
        File parentFile = file.getParentFile();
        return parentFile;
      }
    };
}