All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xyz.cofe.fs.FileVisitor Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License
 *
 * Copyright 2014 Kamnev Georgiy ([email protected]).
 *
 * Данная лицензия разрешает, безвозмездно, лицам, получившим копию данного программного 
 * обеспечения и сопутствующей документации (в дальнейшем именуемыми "Программное Обеспечение"), 
 * использовать Программное Обеспечение без ограничений, включая неограниченное право на 
 * использование, копирование, изменение, объединение, публикацию, распространение, сублицензирование 
 * и/или продажу копий Программного Обеспечения, также как и лицам, которым предоставляется 
 * данное Программное Обеспечение, при соблюдении следующих условий:
 *
 * Вышеупомянутый копирайт и данные условия должны быть включены во все копии 
 * или значимые части данного Программного Обеспечения.
 *
 * ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ ЛЮБОГО ВИДА ГАРАНТИЙ, 
 * ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ, 
 * СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И НЕНАРУШЕНИЯ ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ 
 * ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ 
 * ИЛИ ДРУГИХ ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ 
 * ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ 
 * ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
 */

package xyz.cofe.fs;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import xyz.cofe.collection.Convertor;
import xyz.cofe.collection.Iterators;
import xyz.cofe.collection.NodesExtracter;
import xyz.cofe.collection.Predicate;
import xyz.cofe.collection.iterators.TreeWalk;
import xyz.cofe.collection.iterators.TreeWalkItreator;

/**
 * Осуществляет обход каталога
 * @author Kamnev Georgiy ([email protected])
 */
public class FileVisitor{
    //
    private static void logFine(String message,Object ... args){
        Logger.getLogger(FileVisitor.class.getName()).log(Level.FINE, message, args);
    }
    
    private static void logFiner(String message,Object ... args){
        Logger.getLogger(FileVisitor.class.getName()).log(Level.FINER, message, args);
    }
    
    private static void logFinest(String message,Object ... args){
        Logger.getLogger(FileVisitor.class.getName()).log(Level.FINEST, message, args);
    }
    
    private static void logInfo(String message,Object ... args){
        Logger.getLogger(FileVisitor.class.getName()).log(Level.INFO, message, args);
    }

    private static void logWarning(String message,Object ... args){
        Logger.getLogger(FileVisitor.class.getName()).log(Level.WARNING, message, args);
    }
    
    private static void logSevere(String message,Object ... args){
        Logger.getLogger(FileVisitor.class.getName()).log(Level.SEVERE, message, args);
    }

    private static void logException(Throwable ex){
        Logger.getLogger(FileVisitor.class.getName()).log(Level.SEVERE, null, ex);
    }
    //

    /**
     * Конструктор по умолчанию
     */
    public FileVisitor(){
    }
    
    /**
     * Поведение при возниконовеии ошибки
     */
    public static enum ErrorBehavior {
        /**
         * Продолжить выполнение
         */
        Continue,
        
        /**
         * Завершить работу
         */
        Stop
    }
    
    /**
     * Опции копирования
     */
    public static class CopyOptions {
        public boolean checkHistory = true;
        public boolean history = false;
        public boolean followLink = true;
        public boolean resolveLink = true;
        public boolean maxRecursiveLinkLevel = true;
        public boolean useCanonicalPath = true;
        public boolean useAbsolutePath = true;
        public boolean errorBehavior = true;
        public boolean filter = true;
    }
    
    /**
     * Конструктор копирования
     * @param src образец
     * @param opts Опции копирования
     */
    public FileVisitor( FileVisitor src, CopyOptions opts ){
        if( src!=null && opts!=null ){
            if( opts.checkHistory )checkHistory = src.isCheckHistory();
            if( opts.history ){
                visitHistory.clear();
                visitHistory.addAll(src.getVisitHistory());
            }
            if( opts.resolveLink ){
                this.resolveLink = src.isResolveLink();
            }
            if( opts.followLink ){
                this.followLink = src.isFollowLink();
            }
            if( opts.maxRecursiveLinkLevel ){
                this.maxRecusiveLinkLevel = src.getMaxRecusiveLinkLevel();
            }
            if( opts.useCanonicalPath ){
                this.useCanonicalPath = src.isUseCanonicalPath();
            }
            if( opts.useAbsolutePath ){
                this.useAbsolutePath = src.isUseAbsolutePath();
            }
            if( opts.filter ){
                this.filter = src.filter;
            }
        }
    }
    
    /**
     * Клонирует объект
     * @return копия объекта
     */
    @Override
    public FileVisitor clone(){
        CopyOptions opts = new CopyOptions();
        return clone(opts);
    }
    
    /**
     * Клонирует объект
     * @param opts Опции копирования
     * @return копия объекта
     */
    public FileVisitor clone(CopyOptions opts){
        return new FileVisitor( this, opts );
    }
    
    /**
     * Вызывается при входе в каталог/файл
     * @param path Путь/файл в который осуществляется вход
     */
    public void enter(Stack path){
    }
    
    /**
     * Вызывается при выходе из каталога/файла
     * @param path Путь/файл из которого осуществляется выход
     */
    public void exit(Stack path){
    }
    
    /**
     * Вызывается при возниконовении ошибки
     * @param err Ошибка
     * @return true - продолжить выполнение, false - завершить работу
     */
    public ErrorBehavior error(Throwable err){
        System.err.println("error "+err.getMessage());
        return errorBehavior;
    }
    
    //
    private Set visitHistory = new TreeSet();
    
    /**
     * Возвращает набор посещенных файлов/каталогов
     * @return Список файлов
     */
    public Set getVisitHistory() {
        if( visitHistory==null )visitHistory = new TreeSet();
        return visitHistory;
    }
    //
    
    //
    private ErrorBehavior errorBehavior = ErrorBehavior.Continue;
    
    /**
     * Поведение при возникновении ошибки
     * @return поведение
     */
    public ErrorBehavior getErrorBehavior() {
        return errorBehavior;
    }
    
    /**
     * Поведение при возникновении ошибки
     * @param errorBehavior поведение
     */
    public void setErrorBehavior(ErrorBehavior errorBehavior) {
        this.errorBehavior = errorBehavior;
    }
    //
    
    //
    private boolean checkHistory = true;
    
    /**
     * Проверять историю посещенных каталог/файлов, что бы избежать бесконечных циклов.
     * По умолчанию true.
     * @return Проверять историю посещенных каталог/файлов
     */
    public boolean isCheckHistory() {
        return checkHistory;
    }
    
    /**
     * Проверять историю посещенных каталог/файлов, что бы избежать бесконечных циклов.
     * @param checkHistory Проверять историю посещенных каталог/файлов
     */
    public void setCheckHistory(boolean checkHistory) {
        this.checkHistory = checkHistory;
    }
    //
    
    //
    private boolean followLink = true;

    /**
     * Следовать символическим ссылкам. По умолчанию true. 
* Если true - то, с. ссылки будут испоьзоватся в качестве ссылок на каталоги * @return Следовать символическим ссылкам. */ public boolean isFollowLink() { return followLink; } /** * Следовать символическим ссылкам. * @param followLink Следовать символическим ссылкам. */ public void setFollowLink(boolean followLink) { this.followLink = followLink; } //
// private boolean resolveLink = false; /** * "Разрешать" символические ссылки. По умолчанию false.
* Если установлено в true - то, при нахождении символической ссылки будет установлено куда она указывает * и использовано разрешенное имя.
*
* Пример:
* Есть следующая структура файлов
*
* /A/
* /A/1
* /A/link -> /B
* /B/
* /B/2
* /B/3
*
* * * * * * * * *
resolveLink = falseresolveLink = true
* /A/
* /A/1
* /A/link/
* /A/link/2
* /A/link/3
* /B/
* /B/2
* /B/3
*
* /A/
* /A/1
* /B/
* /B/2
* /B/3
*
* @return "Разрешать" символические ссылки. */ public boolean isResolveLink() { return resolveLink; } /** * "Разрешать" символические ссылки. * @param resolveLink "Разрешать" символические ссылки. * @see #isResolveLink() */ public void setResolveLink(boolean resolveLink) { this.resolveLink = resolveLink; } //
// private int maxRecusiveLinkLevel = 100; /** * Максимальная глубина вложенности символических ссылок.
* -1 - не органичено. * По умолчанию 100 * @return Максимальная глубина вложенности символических ссылок */ public int getMaxRecusiveLinkLevel() { return maxRecusiveLinkLevel; } /** * Максимальная глубина вложенности символических ссылок.
* @param maxRecusiveLinkLevel Максимальная глубина вложенности символических ссылок.
-1 - не органичено. */ public void setMaxRecusiveLinkLevel(int maxRecusiveLinkLevel) { this.maxRecusiveLinkLevel = maxRecusiveLinkLevel; } //
// private boolean useCanonicalPath = true; /** * Приводить пути файлов к канонической форме, по умолч. true. * @return Приводить пути файлов к канонической форме */ public boolean isUseCanonicalPath() { return useCanonicalPath; } /** * Приводить пути файлов к канонической форме * @param useCanonicalPath Приводить пути файлов к канонической форме */ public void setUseCanonicalPath(boolean useCanonicalPath) { this.useCanonicalPath = useCanonicalPath; } // // private boolean useAbsolutePath = false; /** * Приводить пути файлов к абсолютной форме, по умолч. false. * @return Приводить пути файлов к абсолютной форме */ public boolean isUseAbsolutePath() { return useCanonicalPath; } /** * Приводить пути файлов к абсолютной форме * @param useCanonicalPath Приводить пути файлов к абсолютной форме */ public void setUseAbsolutePath(boolean useCanonicalPath) { this.useCanonicalPath = useCanonicalPath; } // // /** * Рекурсивно разрешает ссылки * @param rl Получение ссылки * @param currentResolve текущая итерация * @param maxResolve макс. кол-во итераций или -1 * @return Ссылка или null */ protected File resolveLink( ResolveLink rl, int currentResolve, int maxResolve ){ if( currentResolve>maxResolve && maxResolve>=0 )return null; File f = rl.resolveLink(); if( f==null )return null; if( (f instanceof IsSymbolicLink) && (f instanceof ResolveLink) ){ ResolveLink frl = (ResolveLink)f; IsSymbolicLink fis = (IsSymbolicLink)f; if( fis.isSymbolicLink() ){ File rf = resolveLink(frl,currentResolve+1,maxResolve); if( rf==null ){ return f; } return rf; } } return f; } // // protected Predicate> filter = null; /** * Вызывается перед входом в каталог для проверки необходимости войти * @return Предикат входа в каталог */ public Predicate> getFilter() { return filter; } /** * Вызывается перед входом в каталог для проверки необходимости войти * @param filter Предикат входа в каталог */ public void setFilter(Predicate> filter) { this.filter = filter; } // private List getChildrenOf_post( List children ){ for( int i=0; i getChildrenOf( File parent){ ArrayList res = new ArrayList(); if( parent==null )return res; if( isCheckHistory() ){ Set hist = getVisitHistory(); if( hist.contains(parent) )return res; hist.add(parent); } if( parent instanceof IsSymbolicLink ){ boolean islink = ((IsSymbolicLink)parent).isSymbolicLink(); if( islink ){ if( !isFollowLink() ){ return res; } if( isResolveLink() && (parent instanceof ResolveLink) ){ File resolved = resolveLink((ResolveLink)parent,1,getMaxRecusiveLinkLevel()); if( resolved!=null ){ if( resolved.isDirectory() ){ List files = resolved.listFiles(); return getChildrenOf_post(files); }else{ // res.add( isUseCanonicalPath() ? // (T)resolved.getCanonical() : // resolved ); return getChildrenOf_post(res); } } } } } if( parent.isDirectory() ){ List files = parent.listFiles(); for( File f : files ){ res.add( f ); } } return getChildrenOf_post(res); } private boolean walk_isRunning = false; /** * Осуществляет обход каталога, * вызывает методы enter/exit * @param root точка с которой начиается обход * @see #enter(java.util.Stack) * @see #exit(java.util.Stack) */ public void walk( File root ){ if( root==null )throw new IllegalArgumentException( "root==null" ); if( walk_isRunning )throw new IllegalStateException("walk is running"); try{ walk_isRunning = true; getVisitHistory().clear(); Stack s = new Stack(); s.push( isUseCanonicalPath() ? (File)root.getCanonical() : root); walk(s); s.pop(); }finally{ walk_isRunning = false; } } private int walk( Stack s ){ int res = 0; if( filter!=null && !filter.validate(s) ){ return res; } enter(s); if( !s.empty() ){ File p = s.peek(); boolean hasErr = false; boolean stop = false; Iterable children = null; try{ children = getChildrenOf(p); }catch( Throwable err ){ hasErr = true; ErrorBehavior eb = error(err); if( eb!=null ){ switch( eb ){ case Continue: stop = false; break; case Stop: stop = true; break; } } } if( !hasErr ){ if( children!=null ){ for( File f : children ){ s.push(f); int r = walk(s); if( r!=0 ){ s.pop(); res = 1; break; } s.pop(); } } }else{ if( stop ){ res = 1; }else{ res = 0; } } } exit(s); return res; } public Iterable> treeWalkIterable( File root ){ if( root==null )throw new IllegalArgumentException( "root==null" ); final FileVisitor fv = clone(); NodesExtracter ne = new NodesExtracter() { @Override public Iterable extract(File from) { Iterable itr = null; try{ itr = fv.getChildrenOf(from); }catch(Throwable err){ fv.error(err); itr = null; } return itr; } }; fv.getVisitHistory().clear(); File froot = root; if( isUseAbsolutePath() ){ froot = (File)froot.getAbsolute(); } if( isUseCanonicalPath() ){ File canon = (File)froot.getCanonical(); froot = canon; } // return Iterators.tree(froot, ne); // TreeIterable treeItreable = new TreeIterable(ne, ne); final Stack stack = new Stack(); Iterable> itr = TreeWalkItreator.createIterable(froot, ne, new Predicate>(){ @Override public boolean validate(TreeWalk value) { if( fv.filter==null )return true; stack.clear(); for( File n : value.nodePath() ){ stack.add(n); } return fv.filter.validate(stack); } } ); return itr; } /** * Возвращает последовательность файлов/каталогов (включая вложенгные) * @param root точка с которой начиается обход * @return последовательность файлов/каталогов */ public Iterable walkIterable( File root ){ if( root==null )throw new IllegalArgumentException( "root==null" ); // final FileVisitor fv = clone(); // NodesExtracter ne = new NodesExtracter() { // @Override // public Iterable extract(File from) { // Iterable itr = null; // try{ // itr = fv.getChildrenOf(from); // }catch(Throwable err){ // fv.error(err); // itr = null; // } // return itr; // } // }; // fv.getVisitHistory().clear(); // // File froot = root; // if( isUseAbsolutePath() ){ // froot = (File)froot.getAbsolute(); // } // if( isUseCanonicalPath() ){ // File canon = (File)froot.getCanonical(); // froot = canon; // } // //// return Iterators.tree(froot, ne); //// TreeIterable treeItreable = new TreeIterable(ne, ne); // // final Stack stack = new Stack(); // // Iterable> itr = TreeWalkItreator.createIterable(froot, // ne, // new Predicate>(){ // @Override // public boolean validate(TreeWalk value) { // if( fv.filter==null )return true; // stack.clear(); // for( File n : value.nodePath() ){ // stack.add(n); // } // return fv.filter.validate(stack); // } // } // ); Iterable> itr = treeWalkIterable(root); Iterable resitr = Iterators.convert(itr, new ConvertTreeWalk()); return resitr; } public static class ConvertTreeWalk implements Convertor, T> { @Override public T convert(TreeWalk from) { return from.currentNode(); } // @Override // public T convert(Iterable> from) { // return from. // } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy