content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.chemistry.opencmis.workbench.model;
  
  import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNotEmpty;
  import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNullOrEmpty;
  
  import java.io.BufferedInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.swing.event.EventListenerList;
  
  import org.apache.chemistry.opencmis.client.api.CmisObject;
  import org.apache.chemistry.opencmis.client.api.FileableCmisObject;
  import org.apache.chemistry.opencmis.client.api.Folder;
  import org.apache.chemistry.opencmis.client.api.ItemIterable;
  import org.apache.chemistry.opencmis.client.api.ObjectId;
  import org.apache.chemistry.opencmis.client.api.ObjectType;
  import org.apache.chemistry.opencmis.client.api.OperationContext;
  import org.apache.chemistry.opencmis.client.api.QueryResult;
  import org.apache.chemistry.opencmis.client.api.Session;
  import org.apache.chemistry.opencmis.client.api.Tree;
  import org.apache.chemistry.opencmis.client.runtime.OperationContextImpl;
  import org.apache.chemistry.opencmis.commons.PropertyIds;
  import org.apache.chemistry.opencmis.commons.data.ContentStream;
  import org.apache.chemistry.opencmis.commons.data.RepositoryCapabilities;
  import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
  import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
  import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery;
  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
  import org.apache.chemistry.opencmis.commons.enums.VersioningState;
  import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
  import org.apache.chemistry.opencmis.commons.impl.IOUtils;
  import org.apache.chemistry.opencmis.commons.impl.MimeTypes;
  import org.apache.chemistry.opencmis.workbench.LoggingInputStream;
  import org.apache.chemistry.opencmis.workbench.RandomInputStream;
  
  public class ClientModel {
  
      // object details must not be older than 60 seconds
      private static final long OLD = 60 * 1000;
  
      private ClientSession clientSession;
  
      private Folder currentFolder = null;
      private List<CmisObject> currentChildren = Collections.emptyList();
      private CmisObject currentObject = null;
      private List<ObjectType> baseTypes = null;
  
      private final EventListenerList listenerList = new EventListenerList();
  
      public ClientModel() {
      }
  
      public synchronized void addFolderListener(FolderListener listener) {
          listenerList.add(FolderListener.class, listener);
      }
  
      public synchronized void removeFolderListener(FolderListener listener) {
          listenerList.remove(FolderListener.class, listener);
      }
  
      public synchronized void addObjectListener(ObjectListener listener) {
          listenerList.add(ObjectListener.class, listener);
      }
  
      public synchronized void removeObjectListener(ObjectListener listener) {
          listenerList.remove(ObjectListener.class, listener);
      }
  
      public synchronized void setClientSession(ClientSession clientSession) {
          this.clientSession = clientSession;
          this.currentFolder = null;
          this.currentChildren = Collections.emptyList();
          this.currentObject = null;
          this.baseTypes = null;
      }
  
      public synchronized ClientSession getClientSession() {
          return clientSession;
      }
  
      public RepositoryInfo getRepositoryInfo() {
          Session session = getClientSession().getSession();
          return session.getRepositoryInfo();
      }
  
      public String getRepositoryName() {
          try {
              String name = getRepositoryInfo().getName();
              return name == null ? "(no name)" : name;
          } catch (Exception e) {
              return "?";
          }
      }
  
      public boolean supportsQuery() {
          try {
              RepositoryCapabilities cap = getRepositoryInfo().getCapabilities();
              if (cap == null) {
                  return true;
              }
  
              return (cap.getQueryCapability() != null) && (cap.getQueryCapability() != CapabilityQuery.NONE);
          } catch (Exception e) {
              return false;
          }
      }
  
      public boolean supportsChangeLog() {
          try {
              RepositoryCapabilities cap = getRepositoryInfo().getCapabilities();
              if (cap == null) {
                  return true;
              }
  
              return (cap.getChangesCapability() != null) && (cap.getChangesCapability() != CapabilityChanges.NONE);
          } catch (Exception e) {
              return false;
          }
      }
  
      private synchronized List<ObjectType> getBaseTypes() {
          if (baseTypes == null) {
              baseTypes = new ArrayList<ObjectType>();
              for (ObjectType type : clientSession.getSession().getTypeChildren(null, false)) {
                  baseTypes.add(type);
              }
          }
  
          return baseTypes;
      }
  
      public boolean supportsItems() {
          for (ObjectType type : getBaseTypes()) {
              if (type.getBaseTypeId() == BaseTypeId.CMIS_ITEM) {
                  return true;
              }
          }
  
          return false;
      }
  
      public boolean supportsRelationships() {
          for (ObjectType type : getBaseTypes()) {
              if (type.getBaseTypeId() == BaseTypeId.CMIS_RELATIONSHIP) {
                  return true;
              }
          }
  
          return false;
      }
  
      public boolean supportsPolicies() {
          for (ObjectType type : getBaseTypes()) {
              if (type.getBaseTypeId() == BaseTypeId.CMIS_POLICY) {
                  return true;
              }
          }
  
          return false;
      }
  
      public ObjectId loadFolder(final String folderId, final boolean byPath) {
          try {
              ClientSession myClientSession = getClientSession();
              Session session = myClientSession.getSession();
              CmisObject selectedObject = null;
              CmisObject folderObject = null;
  
              if (byPath) {
                  selectedObject = session.getObjectByPath(folderId);
              } else {
                  selectedObject = session.getObject(folderId);
              }
  
              if (selectedObject instanceof Folder) {
                  folderObject = selectedObject;
              } else if (selectedObject instanceof FileableCmisObject) {
                  List<Folder> parents = ((FileableCmisObject) selectedObject).getParents();
                  if (isNotEmpty(parents)) {
                      folderObject = parents.get(0);
                  }
              }
  
              if (folderObject == null) {
                  // selected object is unfiled, a relationship object, or
                  // the user is not allowed to see the parent folder
                  setCurrentFolder(null, Collections.<CmisObject> emptyList());
                  return selectedObject;
              }
  
              List<CmisObject> children = new ArrayList<CmisObject>();
  
              int maxChildren = myClientSession.getMaxChildren();
              if (maxChildren != 0) {
                  // if maxChildren == 0 don't call getChildren()
                  ItemIterable<CmisObject> iter = ((Folder) folderObject).getChildren(myClientSession
                          .getFolderOperationContext());
  
                  if (myClientSession.getMaxChildren() > 0) {
                      // if maxChildren > 0 restrict number of children
                      // otherwise load all
                      iter = iter.getPage(maxChildren);
                  }
  
                  for (CmisObject child : iter) {
                      children.add(child);
                  }
              }
  
              setCurrentFolder((Folder) folderObject, children);
  
              return selectedObject;
          } catch (CmisBaseException ex) {
              setCurrentFolder(null, new ArrayList<CmisObject>(0));
              throw ex;
          }
      }
  
      public void loadObject(final String objectId) {
          try {
              Session session = getClientSession().getSession();
              CmisObject object = session.getObject(objectId, getClientSession().getObjectOperationContext());
              object.refreshIfOld(OLD);
  
              setCurrentObject(object);
          } catch (CmisBaseException ex) {
              setCurrentObject(null);
              throw ex;
          }
      }
  
      public synchronized void reloadObject() {
          CmisObject myCurrentObject = getCurrentObject();
          if (myCurrentObject == null) {
              return;
          }
  
          ClientSession myClientSession = getClientSession();
          try {
              Session session = myClientSession.getSession();
              CmisObject object = session.getObject(myCurrentObject, myClientSession.getObjectOperationContext());
              object.refresh();
  
              setCurrentObject(object);
          } catch (CmisBaseException ex) {
              setCurrentObject(null);
              throw ex;
          }
      }
  
      public ItemIterable<QueryResult> query(String q, boolean searchAllVersions, int maxHits) {
          OperationContext queryContext = new OperationContextImpl(null, false, false, false, IncludeRelationships.NONE,
                  null, false, null, false, maxHits > 0 ? maxHits : 1);
  
          Session session = getClientSession().getSession();
          return session.query(q, searchAllVersions, queryContext);
      }
  
      public List<Tree<ObjectType>> getTypeDescendants() {
          Session session = getClientSession().getSession();
          return session.getTypeDescendants(null, -1, true);
      }
  
      public ContentStream createContentStream(String filename) throws FileNotFoundException {
          ContentStream content = null;
          if ((filename != null) && (filename.length() > 0)) {
              File file = new File(filename);
              InputStream stream = new LoggingInputStream(new BufferedInputStream(new FileInputStream(file), 512 * 1024),
                      file.getName());
  
              content = getClientSession().getSession().getObjectFactory()
                      .createContentStream(file.getName(), file.length(), MimeTypes.getMIMEType(file), stream);
          }
  
          return content;
      }
  
      public ObjectId createDocument(String name, String type, String filename, Map<String, Object> additionalProperties,
              VersioningState versioningState, boolean unfiled) throws FileNotFoundException {
          Map<String, Object> properties = new HashMap<String, Object>();
          properties.put(PropertyIds.NAME, name);
          properties.put(PropertyIds.OBJECT_TYPE_ID, type);
  
          if (additionalProperties != null) {
              properties.putAll(additionalProperties);
          }
  
          ContentStream content = createContentStream(filename);
  
          try {
              return getClientSession().getSession().createDocument(properties, (unfiled ? null : getCurrentFolder()),
                      content, versioningState, null, null, null);
          } finally {
              IOUtils.closeQuietly(content);
          }
      }
  
      public ContentStream createContentStream(String name, long length, long seed) {
          return getClientSession()
                  .getSession()
                  .getObjectFactory()
                  .createContentStream(name, length, "application/octet-stream",
                          new LoggingInputStream(new RandomInputStream(length, seed), name + " (random)"));
      }
  
      public synchronized ObjectId createDocument(String name, String type, Map<String, Object> additionalProperties,
              long length, long seed, VersioningState versioningState, boolean unfiled) {
          Map<String, Object> properties = new HashMap<String, Object>();
          properties.put(PropertyIds.NAME, name);
          properties.put(PropertyIds.OBJECT_TYPE_ID, type);
  
          if (additionalProperties != null) {
              properties.putAll(additionalProperties);
          }
  
          ContentStream content = createContentStream(name, length, seed);
          try {
              return clientSession.getSession().createDocument(properties, (unfiled ? null : getCurrentFolder()),
                      content, versioningState, null, null, null);
          } finally {
              IOUtils.closeQuietly(content);
          }
      }
  
      public synchronized ObjectId createItem(String name, String type, Map<String, Object> additionalProperties,
              boolean unfiled) {
          Map<String, Object> properties = new HashMap<String, Object>();
          properties.put(PropertyIds.NAME, name);
          properties.put(PropertyIds.OBJECT_TYPE_ID, type);
  
          if (additionalProperties != null) {
              properties.putAll(additionalProperties);
          }
  
          return getClientSession().getSession().createItem(properties, (unfiled ? null : getCurrentFolder()), null,
                  null, null);
      }
  
      public synchronized ObjectId createFolder(String name, String type, Map<String, Object> additionalProperties) {
          Map<String, Object> properties = new HashMap<String, Object>();
          properties.put(PropertyIds.NAME, name);
          properties.put(PropertyIds.OBJECT_TYPE_ID, type);
  
          if (additionalProperties != null) {
              properties.putAll(additionalProperties);
          }
  
          return getClientSession().getSession().createFolder(properties, getCurrentFolder(), null, null, null);
      }
  
      public synchronized ObjectId createRelationship(String name, String type, String sourceId, String targetId,
              Map<String, Object> additionalProperties) {
          Map<String, Object> properties = new HashMap<String, Object>();
          properties.put(PropertyIds.NAME, name);
          properties.put(PropertyIds.OBJECT_TYPE_ID, type);
          properties.put(PropertyIds.SOURCE_ID, sourceId);
          properties.put(PropertyIds.TARGET_ID, targetId);
  
          if (additionalProperties != null) {
              properties.putAll(additionalProperties);
          }
  
          return getClientSession().getSession().createRelationship(properties, null, null, null);
      }
  
      public synchronized ObjectId createPolicy(String name, String type, String policyText,
              Map<String, Object> additionalProperties, boolean unfiled) {
          Map<String, Object> properties = new HashMap<String, Object>();
          properties.put(PropertyIds.NAME, name);
          properties.put(PropertyIds.OBJECT_TYPE_ID, type);
          if (policyText != null && policyText.length() > 0) {
              properties.put(PropertyIds.POLICY_TEXT, policyText);
          }
  
          if (additionalProperties != null) {
              properties.putAll(additionalProperties);
          }
  
          return getClientSession().getSession().createPolicy(properties, (unfiled ? null : getCurrentFolder()), null,
                  null, null);
      }
  
      public synchronized List<ObjectType> getTypesAsList(String rootTypeId, boolean creatableOnly) {
          Session session = getClientSession().getSession();
  
          List<ObjectType> result = new ArrayList<ObjectType>();
  
          ObjectType rootType = null;
          try {
              rootType = session.getTypeDefinition(rootTypeId);
          } catch (CmisBaseException e) {
              return result;
          }
  
          List<Tree<ObjectType>> types = session.getTypeDescendants(rootTypeId, -1, true);
          addType(types, result, creatableOnly);
  
          if (creatableOnly) {
              boolean isCreatable = (rootType.isCreatable() == null ? true : rootType.isCreatable().booleanValue());
              if (isCreatable) {
                  result.add(rootType);
              }
          } else {
              result.add(rootType);
          }
  
          Collections.sort(result, new Comparator<ObjectType>() {
              @Override
              public int compare(ObjectType ot1, ObjectType ot2) {
                  return ot1.getDisplayName().compareTo(ot2.getDisplayName());
              }
          });
  
          return result;
      }
  
      private void addType(List<Tree<ObjectType>> types, List<ObjectType> resultList, boolean creatableOnly) {
          assert types != null;
          assert resultList != null;
  
          for (Tree<ObjectType> tt : types) {
              if (tt.getItem() != null) {
                  if (creatableOnly) {
                      boolean isCreatable = (tt.getItem().isCreatable() == null ? true : tt.getItem().isCreatable()
                              .booleanValue());
                      if (isCreatable) {
                          resultList.add(tt.getItem());
                      }
                  } else {
                      resultList.add(tt.getItem());
                  }
  
                  addType(tt.getChildren(), resultList, creatableOnly);
              }
          }
      }
  
      public synchronized Folder getCurrentFolder() {
          return currentFolder;
      }
  
      public synchronized List<CmisObject> getCurrentChildren() {
          return currentChildren;
      }
  
      public synchronized CmisObject getFromCurrentChildren(String id) {
          if (isNullOrEmpty(currentChildren)) {
              return null;
          }
  
          for (CmisObject o : currentChildren) {
              if (o.getId().equals(id)) {
                  return o;
              }
          }
  
          return null;
      }
  
      private synchronized void setCurrentFolder(Folder folder, List<CmisObject> children) {
          currentFolder = folder;
          currentChildren = children;
  
          for (FolderListener fl : listenerList.getListeners(FolderListener.class)) {
              fl.folderLoaded(new ClientModelEvent(this));
          }
      }
  
      public synchronized CmisObject getCurrentObject() {
          return currentObject;
      }
  
      private synchronized void setCurrentObject(CmisObject object) {
          currentObject = object;
  
          for (ObjectListener ol : listenerList.getListeners(ObjectListener.class)) {
              ol.objectLoaded(new ClientModelEvent(this));
          }
      }
  }
  