Coverage Report - org.webslinger.commons.vfs.cow.COWFileSystem
 
Classes in this File Line Coverage Branch Coverage Complexity
COWFileSystem
95%
18/19
100%
1/1
0
COWFileSystem$1
100%
2/2
100%
1/1
0
COWFileSystem$COWResolution
95%
54/57
100%
20/20
0
COWFileSystem$DefaultCOWEntryFactory
71%
15/21
100%
5/5
0
COWFileSystem$DefaultCOWEntryFactory$1
100%
4/4
100%
1/1
0
 
 1  
 package org.webslinger.commons.vfs.cow;
 2  
 
 3  
 import java.io.IOException;
 4  
 import java.net.URLStreamHandler;
 5  
 import java.util.Arrays;
 6  
 import java.util.Collection;
 7  
 import java.util.HashSet;
 8  
 import java.util.Iterator;
 9  
 import java.util.Map;
 10  
 import java.util.concurrent.ConcurrentHashMap;
 11  
 
 12  
 import org.apache.commons.vfs.Capability;
 13  
 import org.apache.commons.vfs.FileName;
 14  
 import org.apache.commons.vfs.FileObject;
 15  
 import org.apache.commons.vfs.FileSystem;
 16  
 import org.apache.commons.vfs.FileSystemException;
 17  
 import org.apache.commons.vfs.FileSystemOptions;
 18  
 import org.apache.commons.vfs.provider.DefaultURLStreamHandler;
 19  
 
 20  
 import org.webslinger.lang.ConcurrentFreezingCache;
 21  
 import org.webslinger.lang.Freezer;
 22  
 import static org.webslinger.lang.ClassUtil.NULL;
 23  
 import org.webslinger.commons.vfs.FileSet;
 24  
 import org.webslinger.commons.vfs.FilteringFileSystem;
 25  
 import org.webslinger.commons.vfs.GenerationalFileObject;
 26  
 import org.webslinger.commons.vfs.GenerationalFileSystem;
 27  
 
 28  388
 public class COWFileSystem extends GenerationalFileSystem<FileName, COWFileObject, COWFileSystem.COWResolution, COWFileSystem> {
 29  
     protected final COWEntryFactory cowEntryFactory;
 30  
 
 31  6
     protected HashSet<Capability> caps = new HashSet<Capability>();
 32  
 
 33  
     public COWFileSystem(FileName name, FileObject root, FileSystemOptions options) throws FileSystemException {
 34  6
         super(name, root, options);
 35  6
         FilteringFileSystem.copyCapabilities(root.getFileSystem(), caps);
 36  6
         COWEntryFactory cowEntryFactory = COWConfigBuilder.getInstance().getCOWEntryFactory(options);
 37  6
         if (cowEntryFactory == null) cowEntryFactory = new DefaultCOWEntryFactory();
 38  6
         this.cowEntryFactory = cowEntryFactory;
 39  6
     }
 40  
 
 41  9
     public static final class DefaultCOWEntryFactory implements COWEntryFactory {
 42  9
         protected final ConcurrentHashMap<FileSystem, ConcurrentFreezingCache<COWFileObject, Object, COWState>> cowStates = new ConcurrentHashMap<FileSystem, ConcurrentFreezingCache<COWFileObject, Object, COWState>>();
 43  
 
 44  
         public COWEntry getCOWEntry(COWFileSystem cfs, COWFileObject file, boolean create) throws FileSystemException {
 45  
             //if (file.getFileSystem() != cfs) throw new InternalError();
 46  
             //if (file == null) return null;
 47  1402
             COWState state = getCOWState(cfs, file.getParent(), create);
 48  1402
             if (state == null) return null;
 49  1377
             return state.getEntry(file.getName().getBaseName(), create);
 50  
         }
 51  
 
 52  
         public String[] getChildNames(COWFileSystem cfs, COWFileObject parent, boolean create) throws FileSystemException {
 53  
             //if (parent.getFileSystem() != cfs) throw new InternalError();
 54  
             //if (parent == null) return null;
 55  63
             COWState state = getCOWState(cfs, parent, create);
 56  63
             if (state == null) return null;
 57  58
             return state.getChildNames();
 58  
         }
 59  
 
 60  
         protected COWState getCOWState(final COWFileSystem cfs, COWFileObject parent, boolean create) throws FileSystemException {
 61  
             //if (parent == null) return null;
 62  1465
             ConcurrentFreezingCache<COWFileObject, Object, COWState> cowStatesPerFS = cowStates.get(cfs);
 63  1465
             if (cowStatesPerFS == null && !create) return null;
 64  1435
             if (cowStatesPerFS == null) {
 65  6
                 cowStatesPerFS = new ConcurrentFreezingCache<COWFileObject, Object, COWState>(COWFileSystem.class, "cowStatsPerFS", cfs.getRoot().getName().toString(), ConcurrentFreezingCache.SOFT, cowStatesPerFSFreezer) {
 66  
                     protected COWState createValue(Object id, Object frozen) throws Exception {
 67  29
                         COWFileObject parent = (COWFileObject) id;
 68  29
                         if (parent != null) return new COWState(cfs, parent.resolveFile(".cowstate.xml"));
 69  5
                         return new COWState(cfs, cfs.resolveFile(".cowstate.xml"));
 70  
                     }
 71  
                 };
 72  6
                 ConcurrentFreezingCache<COWFileObject, Object, COWState> newCowStatesPerFS = cowStates.putIfAbsent(cfs, cowStatesPerFS);
 73  6
                 if (newCowStatesPerFS != null) cowStatesPerFS = newCowStatesPerFS;
 74  
             }
 75  
             try {
 76  1435
                 return cowStatesPerFS.get(parent);
 77  0
             } catch (FileSystemException e) {
 78  0
                 throw e;
 79  0
             } catch (RuntimeException e) {
 80  0
                 throw e;
 81  0
             } catch (Exception e) {
 82  0
                 throw (InternalError) new InternalError("SNO: " + e.getMessage()).initCause(e);
 83  
             }
 84  
         }
 85  
     }
 86  
 
 87  1
     private static final Freezer<COWFileObject, Object> cowStatesPerFSFreezer = new Freezer<COWFileObject, Object>() {
 88  
         public Object freeze(COWFileObject parent) {
 89  1435
             return parent != null ? parent.getName() : NULL;
 90  
         }
 91  
     };
 92  
 
 93  
     public void addCapabilities(Collection caps) {
 94  6
         caps.addAll(this.caps);
 95  6
     }
 96  
 
 97  
     protected COWFileObject createFile(FileName name) {
 98  114
         return new COWFileObject(name, this);
 99  
     }
 100  
 
 101  
     // FIXME: Add listeners to source files, to clear cache
 102  
 
 103  
     String[] getChildNames(COWFileObject file, boolean create) throws FileSystemException {
 104  63
         return cowEntryFactory.getChildNames(this, file, create);
 105  
     }
 106  
 
 107  
     COWEntry getCOWEntry(COWFileObject file, boolean create) throws FileSystemException {
 108  1239
         return cowEntryFactory.getCOWEntry(this, file, create);
 109  
     }
 110  
 
 111  
     public COWEntry getCOWEntry(String path) throws FileSystemException {
 112  163
         return cowEntryFactory.getCOWEntry(this, resolveFile(path), true);
 113  
     }
 114  
 
 115  
     protected int newGeneration() {
 116  47
         return super.newGeneration();
 117  
     }
 118  
 
 119  
     protected COWResolution newResolution(int generation, COWFileObject genFile) throws FileSystemException {
 120  197
         return new COWResolution(generation, genFile);
 121  
     }
 122  
 
 123  
     protected COWFileObject[] newArray(int length) {
 124  71
         return new COWFileObject[length];
 125  
     }
 126  
 
 127  
     protected FileSet getRealFiles(COWFileObject file) throws FileSystemException {
 128  0
         return createFileSet(file.getAllFiles(), new FileObject[0]);
 129  
     }
 130  
 
 131  
     protected class COWResolution extends GenerationalFileSystem.Resolution<FileName, COWFileObject, COWResolution, COWFileSystem> {
 132  
         private final COWFileObject[] files;
 133  
         private final COWEntry[] cowEntries;
 134  
         private final int[] cowEntrySerials;
 135  
 
 136  197
         protected COWResolution(int generation, COWFileObject genFile) throws FileSystemException {
 137  197
             super(generation, genFile);
 138  197
             COWResolver resolver = new COWResolver(COWFileSystem.this);
 139  197
             resolver.resolve(genFile);
 140  197
             files = resolver.getFiles();
 141  197
             for (FileObject file: files) file.refresh();
 142  197
             cowEntries = resolver.getCOWEntries();
 143  197
             cowEntrySerials = resolver.getCOWEntrySerials();
 144  197
         }
 145  
 
 146  
         protected boolean refresh() throws FileSystemException {
 147  345
             int i = files[0] == file ? 1 : 0;
 148  588
             for (; i < files.length; i++) files[i].refresh();
 149  345
             return super.refresh();
 150  
         }
 151  
 
 152  
         FileObject[] getFiles() {
 153  0
             return files;
 154  
         }
 155  
 
 156  
         protected boolean isWriteable() {
 157  28
             return true;
 158  
         }
 159  
 
 160  
         protected boolean isOutOfDate(boolean create) throws FileSystemException {
 161  870
             if (create && files.length == 0) return true;
 162  
             //System.err.println(this + ".isOutOfDate(" + cowStates.length + "):" + java.util.Arrays.asList(cowStates));
 163  4307
             for (int i = 0; i < cowEntries.length; i++) {
 164  3437
                 if (cowEntries[i].checkSerial(cowEntrySerials[i])) return true;
 165  
             }
 166  870
             return false;
 167  
         }
 168  
 
 169  
         protected FileObject doGetFile(boolean create) throws FileSystemException {
 170  207
             COWEntry entry = getCOWEntry(file, create);
 171  207
             if (create) {
 172  48
                 if (entry == null) System.err.println("null: " + file);
 173  48
                 entry.setDeleted(false);
 174  48
                 return files[0].getParentLayerFile();
 175  
             }
 176  
             //System.err.println(this + ": cowEntry=" + getCOWEntry());
 177  159
             if (entry != null && entry.isDeleted()) return null;
 178  230
             for (COWFileObject file: files) {
 179  179
                 FileObject parentFile = file.getParentLayerFile();
 180  179
                 if (parentFile.exists()) return parentFile;
 181  
             }
 182  51
             return null;
 183  
         }
 184  
 
 185  
         protected void doDelete() throws FileSystemException {
 186  8
             getCOWEntry(file, true).setDeleted(true);
 187  8
             files[0].getParentLayerFile().delete();
 188  8
         }
 189  
 
 190  
         protected String[] getChildNames() throws FileSystemException {
 191  
             //System.err.println(this + ".getChildren(): children=" + children);
 192  63
             HashSet<String> names = new HashSet<String>();
 193  167
             for (int i = files.length - 1; i >= 0; i--) {
 194  104
                 COWFileObject cowFile = files[i];
 195  104
                 FileObject fo = cowFile.getParentLayerFile();
 196  104
                 if (fo.getType().hasChildren()) {
 197  95
                     fo.refresh();
 198  95
                     COWFileObject parentDir = cowFile.getParent();
 199  95
                     FileObject[] children = fo.getChildren();
 200  224
                     for (FileObject child: children) {
 201  129
                         String name = child.getName().getBaseName();
 202  129
                         COWEntry childEntry = getCOWEntry(parentDir != null ? parentDir.resolveFile(name) : resolveFile(name), false);
 203  129
                         if (childEntry != null && childEntry.isDeleted()) {
 204  0
                             names.remove(name);
 205  
                         } else {
 206  129
                             names.add(name);
 207  
                         }
 208  
                     }
 209  95
                 } else if (fo.getType().hasContent()) {
 210  0
                     names.add(fo.getName().getBaseName());
 211  
                 }
 212  
             }
 213  63
             String[] foundNames = COWFileSystem.this.getChildNames(file, false);
 214  63
             if (foundNames != null) names.addAll(Arrays.asList(foundNames));
 215  63
             names.remove(".cowstate.xml");
 216  63
             Iterator<String> it = names.iterator();
 217  171
             while (it.hasNext()) {
 218  108
                 String name = it.next();
 219  108
                 COWEntry childEntry = getCOWEntry(file.getName().getPath() + '/' + name);
 220  108
                 if (childEntry.isDeleted()) it.remove();
 221  108
             }
 222  63
             return names.toArray(new String[names.size()]);
 223  
         }
 224  
     }
 225  
 }
 226