/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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.
*
*
* Copyright 2006-2008 Pentaho Corporation. All rights reserved.
*
* Created Feb 8, 2006
* @author wseyler
*/
package org.pentaho.platform.repository.solution.dbbased;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.pentaho.platform.api.engine.IAclSolutionFile;
import org.pentaho.platform.api.engine.IFileFilter;
import org.pentaho.platform.api.engine.IPentahoAclEntry;
import org.pentaho.platform.api.engine.ISolutionFile;
import org.pentaho.platform.api.repository.ISearchable;
import org.pentaho.platform.api.repository.ISolutionRepository;
import org.pentaho.platform.repository.hibernate.HibernateUtil;
import org.pentaho.platform.repository.messages.Messages;
import org.pentaho.platform.util.UUIDUtil;
import org.springframework.security.acl.basic.AclObjectIdentity;
@SuppressWarnings("deprecation")
public class RepositoryFile implements ISearchable, Comparable, AclObjectIdentity, IAclSolutionFile, ISolutionFile {
public static final char EXTENSION_CHAR = '.';
private static final long serialVersionUID = -4129429077568560627L;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
private static final String[] SearchableColumns = { Messages.getInstance().getString("SolutionRepository.QUERY_COLUMN_NAME"), //$NON-NLS-1$
Messages.getInstance().getString("SolutionRepository.QUERY_COLUMN_PATH"), //$NON-NLS-1$
Messages.getInstance().getString("SolutionRepository.QUERY_COLUMN_PARENT") //$NON-NLS-1$
};
private static final String SearchableTable = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile"; //$NON-NLS-1$
private static final String SearchablePhraseNamedQuery = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile.folderSearcher"; //$NON-NLS-1$
protected int revision;
protected String fileId;
protected RepositoryFile parent;
protected String fileName;
protected String fullPath;
protected long lastModified;
protected boolean directory = true;
private byte[] data = null;
private Set childrenFiles = new TreeSet();
private List<IPentahoAclEntry> accessControls = new ArrayList<IPentahoAclEntry>();
public RepositoryFile() {
super();
}
public RepositoryFile(final String fileName, final RepositoryFile parent, final byte[] data) {
this(fileName, parent, data, System.currentTimeMillis());
}
public RepositoryFile(final String fileName, final RepositoryFile parent, final byte[] data, final long lastModified) {
this();
this.fileId = UUIDUtil.getUUIDAsString();
this.fileName = fileName;
if (parent != null) {
parent.addChildFile(this);
}
setParent(parent);
setData(data);
setLastModified(lastModified);
directory = data == null;
}
@Override
public int hashCode() {
return fileId.hashCode();
}
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
}
if (!(other instanceof RepositoryFile)) {
return false;
}
final RepositoryFile that = (RepositoryFile) other;
return getFileId().equals(that.getFileId());
}
protected void resolvePath() {
StringBuffer buffer = new StringBuffer(RepositoryFile.EMPTY_STRING);
if (parent != null) {
buffer.append(parent.getFullPath());
}
buffer.append(ISolutionRepository.SEPARATOR);
buffer.append(fileName);
setFullPath(buffer.toString());
}
public List<IPentahoAclEntry> getAccessControls() {
HibernateUtil.beginTransaction(); // Make sure we're in a transaction
return this.accessControls;
}
/**
* This method's purpose is to allow Hibernate to initialize the ACLs from the data-store. Application clients should likely use resetAccessControls.
*/
public void setAccessControls(final List<IPentahoAclEntry> acls) {
HibernateUtil.beginTransaction(); // Make sure we're in a transaction
this.accessControls = acls;
}
public void resetAccessControls(final List<IPentahoAclEntry> acls) {
HibernateUtil.beginTransaction(); // Makes sure we're within a transaction
if (this.accessControls != null) {
this.accessControls.clear();
this.accessControls.addAll(acls);
}
}
public int getRevision() {
return revision;
}
protected void setRevision(final int revision) {
this.revision = revision;
}
public String getFileId() {
return fileId;
}
protected void setFileId(final String fileId) {
this.fileId = fileId;
}
public String getSolution() {
return getTopFolder().getFileName();
}
public String getSolutionPath() {
ArrayList pathList = new ArrayList();
ISolutionFile folder = parent;
while (!folder.isRoot() && folder.retrieveParent() != null) {
pathList.add(folder.getFileName());
folder = folder.retrieveParent();
}
StringBuffer buffer = new StringBuffer(RepositoryFile.EMPTY_STRING);
for (int i = pathList.size() - 1; i >= 0; i--) {
buffer.append(ISolutionRepository.SEPARATOR);
buffer.append(pathList.get(i).toString());
}
return buffer.toString();
}
public String getFileName() {
return fileName;
}
protected void setFileName(final String fileName) {
this.fileName = fileName;
resolvePath();
}
public String getFullPath() {
return fullPath;
}
protected void setFullPath(final String fullPath) {
this.fullPath = fullPath;
}
public void setParent(final RepositoryFile parent) {
this.parent = parent;
resolvePath();
}
public RepositoryFile getParent() {
return parent;
}
public ISolutionFile retrieveParent() {
return parent;
}
protected RepositoryFile getTopFolder() {
RepositoryFile topFolder = parent;
if (topFolder == null) {
return this;
}
while (!topFolder.isRoot()) {
topFolder = (RepositoryFile) topFolder.retrieveParent();
}
return topFolder;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.repository.ISearchable#getSearchableColumns()
*/
public String[] getSearchableColumns() {
return RepositoryFile.SearchableColumns;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.repository.ISearchable#getSearchableTable()
*/
public String getSearchableTable() {
return RepositoryFile.SearchableTable;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.repository.ISearchable#getPhraseSearchQueryName()
*/
public String getPhraseSearchQueryName() {
return RepositoryFile.SearchablePhraseNamedQuery;
}
protected void setDirectory(final boolean directory) {
this.directory = directory;
}
protected boolean getDirectory() {
return directory;
}
public boolean isDirectory() {
return getDirectory();
}
/**
* @return Returns the childrenResources.
*/
public Set getChildrenFiles() {
return childrenFiles;
}
/**
* @param childrenResources
* The childrenResources to set.
*/
public void setChildrenFiles(final Set childrenFiles) {
this.childrenFiles = childrenFiles;
}
public void addChildFile(final RepositoryFile file) {
getChildrenFiles().add(file);
}
public void removeChildFile(final RepositoryFile file) {
getChildrenFiles().remove(file);
file.setParent(null); // as of now this file has no parent.
}
/**
* @return Returns the data.
*/
public byte[] getData() {
return data;
}
/**
* @param data
* The data to set.
*/
public void setData(final byte[] data) {
this.data = data;
}
public ISolutionFile[] listFiles(final IFileFilter filter) {
List matchedFiles = new ArrayList();
Object[] objArray = getChildrenFiles().toArray();
for (Object element : objArray) {
if (filter.accept((ISolutionFile) element)) {
matchedFiles.add(element);
}
}
return (ISolutionFile[]) matchedFiles.toArray(new ISolutionFile[] {});
}
public ISolutionFile[] listFiles() {
Set<ISolutionFile> files = getChildrenFiles();
return files.toArray(new ISolutionFile[] {});
}
public RepositoryFile[] listRepositoryFiles() {
RepositoryFile[] files = new RepositoryFile[getChildrenFiles().size()];
Iterator iter = getChildrenFiles().iterator();
int i = 0;
while (iter.hasNext()) {
files[i] = (RepositoryFile) iter.next();
i++;
}
return files;
}
public int compareTo(final Object o) {
if (o == null) {
return 1;
} else if (o instanceof RepositoryFile) {
RepositoryFile that = (RepositoryFile) o;
if ((this.getFullPath() == null) && (that.getFullPath() == null)) {
return 0;
} else if ((this.getFullPath() == null) && (that.getFullPath() != null)) {
return -1;
} else if ((this.getFullPath() != null) && (that.getFullPath() == null)) {
return 1;
} else {
return this.getFullPath().compareTo(((RepositoryFile) o).getFullPath());
}
} else {
return this.getFullPath().compareTo(o.toString());
}
}
/**
* @return Returns the modDate.
*/
public long getLastModified() {
return lastModified;
}
/**
* @param modDate
* The modDate to set.
*/
public void setLastModified(final long modDate) {
this.lastModified = modDate;
}
public boolean containsActions() {
boolean hasActions = false;
if (this.isDirectory()) {
Set children = getChildrenFiles();
Iterator iter = children.iterator();
while (iter.hasNext() && !hasActions) {
RepositoryFile file = (RepositoryFile) iter.next();
hasActions = file.getFileName().toLowerCase().endsWith(".xaction"); //$NON-NLS-1$
}
}
return hasActions;
}
public boolean isRoot() {
return retrieveParent() == null;
}
/**
* @return a boolean indicating if this file has an extension
*/
public boolean hasExtension() {
return fileName.lastIndexOf(RepositoryFile.EXTENSION_CHAR) != -1;
}
/**
* @return the extension (including the . seperator) of this file
*/
public String getExtension() {
return hasExtension() ? fileName.substring(fileName.lastIndexOf(RepositoryFile.EXTENSION_CHAR)) : ""; //$NON-NLS-1$
}
public boolean exists() {
return true;
}
/**
* Chains up to find the access controls that are in force on this object.
* Could end up chaining all the way to the root.
*
* <p>Note that (1) defining no access control entries of your own and (2) removing all of your access control
* entries is indistiguishable in the current design. In #1, we chain up because we inherit. But in #2, it might be
* expected that by explicitly removing all access control entries, the chaining up ends. That is not the case in the
* current design.</p>
*/
public List<IPentahoAclEntry> getEffectiveAccessControls() {
List acls = this.getAccessControls();
if (acls.size() == 0) {
RepositoryFile chain = this;
while (!chain.isRoot() && (acls.size() == 0)) {
chain = (RepositoryFile) chain.retrieveParent();
acls = chain.getAccessControls();
}
}
return acls;
}
}