001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2.operations;
018
019import java.util.ArrayList;
020import java.util.Collection;
021
022import org.apache.commons.vfs2.FileObject;
023import org.apache.commons.vfs2.FileSystemException;
024
025/**
026 * Abstracts implementations of {@link FileOperationProvider}.
027 *
028 * @since 0.1
029 */
030public abstract class AbstractFileOperationProvider implements FileOperationProvider {
031
032    /**
033     * Available operations. Operations could be registered for different schemes. Some operations can work only for
034     * "file" scheme, other - for "svnhttp(s)", "svn", "svnssh", but not for "file", etc. The Map has scheme as a key
035     * and Collection of operations that are available for that scheme.
036     */
037    private final Collection<Class<? extends FileOperation>> operations = new ArrayList<>();
038
039    /**
040     * Constructs a new instance for subclasses.
041     */
042    public AbstractFileOperationProvider() {
043        // empty
044    }
045
046    /**
047     * Add new FileOperation to list of known operations.
048     *
049     * @param operationClass a class implementing FileOperation.
050     * @throws FileSystemException if instances of the class cannot be assigned to FileOperation.
051     */
052    protected final void addOperation(final Class<? extends FileOperation> operationClass) throws FileSystemException {
053        // check validity of passed class
054        if (!FileOperation.class.isAssignableFrom(operationClass)) {
055            throw new FileSystemException("vfs.operation/cant-register.error", operationClass);
056        }
057
058        // ok, lets add it to the list
059        operations.add(operationClass);
060    }
061
062    /**
063     * Gather available operations for the specified FileObject and put them into specified operationsList.
064     *
065     * @param operationsList the list of available operations for the specified FileObject. The operationList contains
066     *            classes of available operations, e.g. Class objects.
067     * @param file the FileObject for which we want to get the list of available operations.
068     * @throws FileSystemException if list of operations cannot be retrieved.
069     */
070    @Override
071    public final void collectOperations(final Collection<Class<? extends FileOperation>> operationsList,
072            final FileObject file) throws FileSystemException {
073        doCollectOperations(operations, operationsList, file);
074    }
075
076    /**
077     * Gather available operations for the specified FileObject and put them into specified operationsList.
078     *
079     * @param availableOperations the list of available operations for the specified FileObject.
080     * @param resultList List to be filled with applicable operations.
081     * @param file the FileObject for which we want to get the list of available operations.
082     * @throws FileSystemException if list of operations cannot be retrieved.
083     * @see #collectOperations(Collection operationsList, FileObject file)
084     */
085    protected abstract void doCollectOperations(Collection<Class<? extends FileOperation>> availableOperations,
086        Collection<Class<? extends FileOperation>> resultList, FileObject file) throws FileSystemException;
087
088    /**
089     * @param file the FileObject for which we need an operation.
090     * @param operationClass the Class which instance we are needed.
091     * @return the required operation instance.
092     * @throws FileSystemException if operation cannot be retrieved.
093     */
094    @Override
095    public final FileOperation getOperation(final FileObject file, final Class<? extends FileOperation> operationClass)
096            throws FileSystemException {
097        return instantiateOperation(file, lookupOperation(operationClass));
098    }
099
100    /**
101     * Gets operation instance for specified FileOperation subclass.
102     *
103     * @param file the file this operation should act on.
104     * @param operationClass the class of a file operation interface to instantiate.
105     * @return a new file operation
106     * @throws FileSystemException if operation cannot be instantiated.
107     */
108    protected abstract FileOperation instantiateOperation(FileObject file, Class<? extends FileOperation> operationClass) throws FileSystemException;
109
110    /**
111     * Find class implementing a specific operation interface.
112     *
113     * @param operationClass the interface which is requested.
114     * @return never returns null
115     * @throws FileSystemException if operationClass is not a known FileOperation interface.
116     */
117    protected final Class<? extends FileOperation> lookupOperation(final Class<? extends FileOperation> operationClass)
118            throws FileSystemException {
119        // check validity of passed class
120        if (!FileOperation.class.isAssignableFrom(operationClass)) {
121            throw new FileSystemException("vfs.operation/wrong-type.error", operationClass);
122        }
123
124        // find appropriate class
125        Class<? extends FileOperation> foundClass = null;
126        for (final Class<? extends FileOperation> operation : operations) {
127            if (operationClass.isAssignableFrom(operation)) {
128                foundClass = operation;
129                break;
130            }
131        }
132
133        if (foundClass == null) {
134            throw new FileSystemException("vfs.operation/not-found.error", operationClass);
135        }
136
137        return foundClass;
138    }
139}