/** * DataCleaner (community edition) * Copyright (C) 2014 Neopost - Customer Information Management * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.datacleaner.repository.file; import java.io.File; import java.io.FileFilter; import java.io.OutputStream; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.metamodel.util.Action; import org.apache.metamodel.util.CollectionUtils; import org.apache.metamodel.util.ToStringComparator; import org.datacleaner.repository.AbstractRepositoryNode; import org.datacleaner.repository.RepositoryFile; import org.datacleaner.repository.RepositoryFolder; import org.datacleaner.repository.RepositoryNode; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; /** * {@link RepositoryFolder} implementation based on a local directory. */ public class FileRepositoryFolder extends AbstractRepositoryNode implements RepositoryFolder { private static final long serialVersionUID = 1L; private final File _file; private final FileRepositoryFolder _parent; private transient LoadingCache<File, RepositoryNode> _childCache; public FileRepositoryFolder(final FileRepositoryFolder parent, final File file) { if (file == null) { throw new IllegalArgumentException("File cannot be null"); } if (!file.exists()) { throw new IllegalArgumentException("File does not exist: " + file); } _parent = parent; _file = file; } private LoadingCache<File, RepositoryNode> getChildCache() { if (_childCache == null) { _childCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS) .build(new CacheLoader<File, RepositoryNode>() { @Override public RepositoryNode load(final File key) throws Exception { if (key.isDirectory()) { return new FileRepositoryFolder(FileRepositoryFolder.this, key); } return new FileRepositoryFile(FileRepositoryFolder.this, key); } }); } return _childCache; } public File getFile() { return _file; } @Override public RepositoryFolder getParent() { return _parent; } @Override public String getName() { return _file.getName(); } @Override public List<RepositoryFolder> getFolders() { final File[] directories = _file.listFiles(file -> { if (file.isDirectory() && !file.isHidden() && !file.getName().startsWith(".")) { return true; } return false; }); //Sort the directories as listFiles does not gurantee an order. Arrays.sort(directories); return CollectionUtils .map(directories, directory -> (RepositoryFolder) getChildCache().getUnchecked(directory)); } @Override public RepositoryFile getLatestFile(final String prefix, final String extension) { final FileFilter baseFilter = createFileFilter(prefix, extension); final LatestFileFilter latestFileFilter = new LatestFileFilter(baseFilter); _file.listFiles(latestFileFilter); final File latestFile = latestFileFilter.getLatestFile(); if (latestFile == null) { return null; } return (RepositoryFile) getChildCache().getUnchecked(latestFile); } @Override public List<RepositoryFile> getFiles(final String prefix, final String extension) { final File[] files = _file.listFiles(createFileFilter(prefix, extension)); Arrays.sort(files, ToStringComparator.getComparator()); return CollectionUtils.map(files, file -> (RepositoryFile) getChildCache().getUnchecked(file)); } private FileFilter createFileFilter(final String prefix, final String extension) { return file -> { if (file.isFile() && !file.isHidden()) { final String filename = file.getName(); if (prefix == null || filename.startsWith(prefix)) { if (extension == null) { return true; } else { return filename.endsWith(extension); } } } return false; }; } @Override public List<RepositoryFile> getFiles() { return getFiles(null, null); } @Override public RepositoryFile getFile(final String name) { if (name.indexOf('/') != -1 || name.indexOf('\\') != -1) { throw new IllegalArgumentException("File name cannot contain slashes"); } final File file = new File(_file, name); if (!file.exists()) { return null; } if (file.isHidden()) { return null; } if (!file.isFile()) { return null; } return (RepositoryFile) getChildCache().getUnchecked(file); } @Override public RepositoryFolder getFolder(final String name) { if (name.indexOf('/') != -1 || name.indexOf('\\') != -1) { throw new IllegalArgumentException("Folder name cannot contain slashes"); } final File file = new File(_file, name); if (!file.exists() || file.isHidden() || !file.isDirectory()) { return null; } return (RepositoryFolder) getChildCache().getUnchecked(file); } @Override public RepositoryFile createFile(final String name, final Action<OutputStream> writeCallback) { if (name.indexOf('/') != -1 || name.indexOf('\\') != -1) { throw new IllegalArgumentException("File name cannot contain slashes"); } final File file = new File(_file, name); if (file.exists()) { throw new IllegalArgumentException("A file with the name '" + name + "' already exists"); } final RepositoryFile repositoryFile = (RepositoryFile) getChildCache().getUnchecked(file); repositoryFile.writeFile(writeCallback); return repositoryFile; } @Override public void delete() throws IllegalStateException { final boolean success = _file.delete(); if (!success) { throw new IllegalStateException("Could not delete directory: " + _file); } _parent.onDeleted(_file); } /** * Notification method invoked when a child file has been deleted. * * @param file */ protected void onDeleted(final File file) { getChildCache().invalidate(file); } @Override public RepositoryFolder createFolder(final String name) { final File file = new File(_file, name); if (file.exists()) { throw new IllegalArgumentException("Folder with name '" + name + "' already exists"); } final boolean result = file.mkdir(); if (!result) { throw new IllegalStateException("Failed to create directory '" + name + "' within " + _file); } return (RepositoryFolder) getChildCache().getUnchecked(file); } @Override public RepositoryFolder getOrCreateFolder(final String name) { final File file = new File(_file, name); if (!file.exists()) { file.mkdir(); } return (RepositoryFolder) getChildCache().getUnchecked(file); } }