You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2013/08/10 07:53:54 UTC

svn commit: r1512568 [38/39] - in /jackrabbit/commons/filevault/trunk: ./ parent/ vault-cli/ vault-cli/src/ vault-cli/src/main/ vault-cli/src/main/appassembler/ vault-cli/src/main/assembly/ vault-cli/src/main/java/ vault-cli/src/main/java/org/ vault-cl...

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/VltEntryInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/VltEntryInfo.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/VltEntryInfo.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/VltEntryInfo.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.apache.jackrabbit.vault.util.MD5;
+
+/**
+ * <code>Entry</code>...
+ *
+ */
+public interface VltEntryInfo {
+
+    public enum Type {
+        BASE,
+        WORK,
+        MINE,
+        THEIRS
+    }
+
+    VltEntryInfo copyAs(Type type);
+
+    Type getType();
+
+    long getDate();
+
+    void setDate(long date);
+
+    MD5 getMd5();
+
+    void setMd5(MD5 md5);
+
+    String getContentType();
+
+    void setContentType(String contentType);
+
+    long getSize();
+
+    void setSize(long size);
+
+    boolean checkModified(VaultFile remoteFile);
+
+    void update(VltEntryInfo base);
+
+    void update(File file, boolean force) throws IOException;
+
+    void update(MetaFile file, boolean force) throws IOException;
+
+    boolean isDirectory();
+    
+    boolean isSame(VltEntryInfo base);
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntries.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntries.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntries.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.vlt.VltFile;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntries;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>BinEntries</code>...
+ */
+public class BinEntries implements VltEntries {
+
+    /**
+     * default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(BinEntries.class);
+
+    protected final BinMetaFile file;
+
+    private String path;
+
+    private Map<String, VltEntry> entries;
+
+    public BinEntries(File file) {
+        this.file = new BinMetaFile(file);
+    }
+
+    public boolean exists() {
+        return file.exists();
+    }
+
+    public void close() {
+        try {
+            file.close();
+        } catch (IOException e) {
+            log.warn("Error while closing entries", e);
+        }
+    }
+
+    public void create(String path) throws IOException {
+        HeaderBlock header = file.open();
+        PropertiesBlock pBlk = file.getLinkedBlock(header, 0, PropertiesBlock.class, true);
+        pBlk.setProperty("path", path);
+        file.sync();
+        this.path = path;
+    }
+
+    private void loadPaths() {
+        if (path == null) {
+            try {
+                HeaderBlock header = file.open();
+                PropertiesBlock pBlk = file.getLinkedBlock(header, 0, PropertiesBlock.class, true);
+                path = pBlk.getProperty("path");
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+
+    public String getPath() {
+        loadPaths();
+        return path;
+    }
+
+    public void update(VltFile file) {
+        VltEntry e = file.getEntry();
+        if (e == null) {
+            BinEntry old = (BinEntry) entries.remove(file.getName());
+            if (old != null) {
+                try {
+                    this.file.delete(old.block);
+                } catch (IOException e1) {
+                    throw new IllegalStateException(e1);
+                }
+            }
+        }
+    }
+
+    public VltEntry getEntry(String localName) {
+        entries();
+        return entries.get(localName);
+    }
+
+    public VltEntry update(String localName, String aggregatePath, String repoRelPath) {
+        entries();
+
+        EntryBlock blk = new EntryBlock(localName, aggregatePath, repoRelPath);
+        BinEntry e = new BinEntry(this, blk);
+
+        BinEntry old = (BinEntry) entries.remove(localName);
+        if (old != null) {
+            e.put(old.work());
+            e.put(old.base());
+            e.put(old.mine());
+            e.put(old.theirs());
+            try {
+                this.file.delete(old.block);
+            } catch (IOException e1) {
+                throw new IllegalStateException(e1);
+            }
+        }
+        file.add(blk);
+        entries.put(localName, e);
+        return e;
+    }
+
+    public boolean hasEntry(String localName) {
+        entries();
+        return entries.containsKey(localName);
+    }
+
+    public Collection<VltEntry> entries() {
+        try {
+            if (entries == null) {
+                entries = new HashMap<String, VltEntry>();
+                file.open();
+                Iterator<Block> iter = file.blocks();
+                while (iter.hasNext()) {
+                    Block blk = iter.next();
+                    if (blk instanceof EntryBlock) {
+                        VltEntry e = new BinEntry(this, (EntryBlock) blk);
+                        entries.put(e.getName(), e);
+                    }
+                }
+            }
+            return entries.values();
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public void dump(DumpContext ctx, boolean isLast) {
+        file.dump(ctx, isLast);
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntry.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntry.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntry.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.apache.jackrabbit.vault.vlt.meta.MetaFile;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntry;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntryInfo;
+
+/**
+ * <code>BinEntry</code>...
+ */
+public class BinEntry implements VltEntry {
+
+    private final BinEntries entries;
+
+    protected final EntryBlock block;
+
+    private Map<VltEntryInfo.Type, VltEntryInfo> infos =
+            new EnumMap<VltEntryInfo.Type, VltEntryInfo>(VltEntryInfo.Type.class);
+
+
+    public BinEntry(BinEntries entries, EntryBlock block) {
+        this.entries = entries;
+        this.block = block;
+
+        try {
+            init(VltEntryInfo.Type.BASE, EntryBlock.ID_BASE);
+            init(VltEntryInfo.Type.WORK, EntryBlock.ID_WORK);
+            init(VltEntryInfo.Type.MINE, EntryBlock.ID_MINE);
+            init(VltEntryInfo.Type.THEIRS, EntryBlock.ID_THEIRS);
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private void init(VltEntryInfo.Type type, int id) throws IOException {
+        InfoBlock blk = entries.file.getLinkedBlock(block, id, InfoBlock.class, false);
+        if (blk != null) {
+            infos.put(type, new BinEntryInfo(type, blk));
+        }
+    }
+    public String getName() {
+        return block.getName();
+    }
+
+    public String getRepoRelPath() {
+        return block.getRelPath();
+    }
+
+    public String getAggregatePath() {
+        return block.getAggregatePath();
+    }
+
+    public VltEntryInfo create(VltEntryInfo.Type type) {
+        return new BinEntryInfo(type, new InfoBlock());
+    }
+
+    public void put(VltEntryInfo info) {
+        if (info == null) {
+            return;
+        }
+        BinEntryInfo old = (BinEntryInfo) infos.put(info.getType(), info);
+        Block newBlk = ((BinEntryInfo) info).block;
+        if (old != null) {
+            // need to save the offset
+            newBlk.offset = old.block.offset;
+        } else {
+            try {
+                switch (info.getType()) {
+                    case BASE:
+                        entries.file.linkBlock(block, EntryBlock.ID_BASE, newBlk);
+                        break;
+                    case MINE:
+                        entries.file.linkBlock(block, EntryBlock.ID_MINE, newBlk);
+                        break;
+                    case THEIRS:
+                        entries.file.linkBlock(block, EntryBlock.ID_THEIRS, newBlk);
+                        break;
+                    case WORK:
+                        entries.file.linkBlock(block, EntryBlock.ID_WORK, newBlk);
+                        break;
+                }
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+
+    public VltEntryInfo work() {
+        return infos.get(VltEntryInfo.Type.WORK);
+    }
+
+    public VltEntryInfo base() {
+        return infos.get(VltEntryInfo.Type.BASE);
+    }
+
+    public VltEntryInfo mine() {
+        return infos.get(VltEntryInfo.Type.MINE);
+    }
+
+    public VltEntryInfo theirs() {
+        return infos.get(VltEntryInfo.Type.THEIRS);
+    }
+
+
+    public VltEntryInfo remove(VltEntryInfo.Type type) {
+        BinEntryInfo old = (BinEntryInfo) infos.remove(type);
+        if (old != null) {
+            switch (type) {
+                case BASE:
+                    block.linkModified(0, EntryBlock.ID_BASE);
+                    break;
+                case MINE:
+                    block.linkModified(0, EntryBlock.ID_MINE);
+                    break;
+                case THEIRS:
+                    block.linkModified(0, EntryBlock.ID_THEIRS);
+                    break;
+                case WORK:
+                    block.linkModified(0, EntryBlock.ID_WORK);
+                    break;
+            }
+        }
+        return old;
+    }
+
+    public State getState() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void resolved(MetaFile fileTmp, File fileWork, MetaFile fileBase) throws IOException {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean delete(File fileWork) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean revertConflict(File work) throws IOException {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void conflict(File work, MetaFile base, MetaFile tmp) throws IOException {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isDirty() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isDirectory() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntryInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntryInfo.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntryInfo.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinEntryInfo.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.apache.jackrabbit.vault.util.MD5;
+import org.apache.jackrabbit.vault.vlt.meta.MetaFile;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntryInfo;
+
+/**
+ * <code>BinEntryInfo</code>...
+ */
+public class BinEntryInfo implements VltEntryInfo {
+
+    protected final Type type;
+
+    protected final InfoBlock block;
+
+    public BinEntryInfo(Type type, InfoBlock block) {
+        this.type = type;
+        this.block = block;
+    }
+
+    public VltEntryInfo copyAs(Type type) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public long getDate() {
+        return block.getDate();
+    }
+
+    public void setDate(long date) {
+        block.setDate(date);
+    }
+
+    public MD5 getMd5() {
+        return block.getMd5();
+    }
+
+    public void setMd5(MD5 md5) {
+        block.setMd5(md5);
+    }
+
+    public String getContentType() {
+        return block.getContentType();
+    }
+
+    public void setContentType(String contentType) {
+        block.setContentType(contentType);
+    }
+
+    public long getSize() {
+        return block.getSize();
+    }
+
+    public void setSize(long size) {
+        block.setSize(size);
+    }
+
+    public boolean checkModified(VaultFile remoteFile) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void update(VltEntryInfo base) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void update(File file, boolean force) throws IOException {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void update(MetaFile file, boolean force) throws IOException {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isDirectory() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isSame(VltEntryInfo base) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaDir.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaDir.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaDir.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaDir.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.vlt.VltException;
+import org.apache.jackrabbit.vault.vlt.meta.MetaDirectory;
+import org.apache.jackrabbit.vault.vlt.meta.MetaFile;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntries;
+
+/**
+ * <code>MetaDir</code>...
+ */
+public class BinMetaDir implements MetaDirectory {
+
+    private BinMetaFile file;
+
+    public BinMetaDir(File file) {
+        this.file = new BinMetaFile(file);
+    }
+
+    public File getFile() {
+        return file.getFile();
+    }
+
+    public String getRepositoryUrl() throws IOException {
+        HeaderBlock hdr = file.open();
+        PropertiesBlock blk = file.getLinkedBlock(hdr, HeaderBlock.ID_PROPERTIES, PropertiesBlock.class, true);
+        return blk.getProperty("repository.url");
+    }
+
+    public void setRepositoryUrl(String url) throws IOException {
+        HeaderBlock hdr = file.open();
+        PropertiesBlock blk = file.getLinkedBlock(hdr, HeaderBlock.ID_PROPERTIES, PropertiesBlock.class, true);
+        blk.setProperty("repository.url", url);
+        file.sync();
+    }
+
+    public boolean exists() {
+        return file.exists();
+    }
+
+    public void create(String path) throws IOException {
+    }
+
+    public void delete() throws IOException {
+    }
+
+    public VltEntries getEntries() throws VltException {
+        return null;
+    }
+
+    public MetaFile getFile(String name) throws IOException {
+        return null;
+    }
+
+    public MetaFile getFile(String name, boolean create) throws IOException {
+        return null;
+    }
+
+    public MetaFile getTmpFile(String name, boolean create) throws IOException {
+        return null;
+    }
+
+    public MetaFile getBaseFile(String name, boolean create) throws IOException {
+        return null;
+    }
+
+    public boolean hasFile(String name) throws IOException {
+        return false;
+    }
+
+    public void sync() throws IOException {
+    }
+
+    public void close() throws IOException {
+    }
+
+    public void dump(DumpContext ctx, boolean isLast) {
+        file.dump(ctx, isLast);
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaFile.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaFile.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaFile.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/BinMetaFile.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,367 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.Dumpable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>MetaFile</code>...
+ */
+public class BinMetaFile implements Dumpable {
+
+    /**
+     * default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(BinMetaFile.class);
+
+    private final File file;
+
+    private RandomAccessFile raf = null;
+
+    private HeaderBlock header = null;
+
+    private Map<Long, Block> blocks = new TreeMap<Long, Block>();
+
+    private Map<Long, Link> links = new TreeMap<Long, Link>();
+
+    private List<Block> added = new LinkedList<Block>();
+
+    public BinMetaFile(File file) {
+        this.file = file;
+    }
+
+    public boolean exists() {
+        return file.exists();
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public HeaderBlock open() throws IOException {
+        if (header == null) {
+            if (!file.exists()) {
+                file.createNewFile();
+            }
+            openRaf();
+            header = new HeaderBlock();
+            if (raf.length() == 0) {
+                header.write(raf);
+            } else {
+                raf.seek(0);
+                header.readData(raf);
+            }
+            blocks.put(0L, header);
+        }
+        return header;
+    }
+
+    public Block getBlock(long offset) throws IOException {
+        open();
+        Block block = blocks.get(offset);
+        if (block == null) {
+            if (offset >= raf.length()) {
+                return null;
+            }
+            raf.seek(offset);
+            block = Block.read(raf);
+            blocks.put(offset, block);
+        }
+        return block;
+    }
+
+    public EntryBlock getEntryBlock(String name) {
+        Iterator<Block> iter = blocks();
+        while (iter.hasNext()) {
+            Block block = iter.next();
+            if (block instanceof EntryBlock) {
+                EntryBlock e = (EntryBlock) block;
+                if (e.getName().equals(name)) {
+                    return e;
+                }
+            }
+        }
+        return null;
+    }
+
+    public Iterator<Block> blocks() {
+        return new Iterator<Block>() {
+
+            private Block next = header;
+            public boolean hasNext() {
+                return next != null;
+            }
+
+            public Block next() {
+                Block ret = next;
+                try {
+                    next = getBlock(ret.next());
+                } catch (IOException e) {
+                    throw new IllegalStateException(e);
+                }
+                return ret;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public DataBlock createDataBlock(long size) throws IOException {
+        DataBlock d = new DataBlock(size);
+        raf.seek(raf.length());
+        d.offset = raf.getFilePointer();
+        d.writeHeader(raf);
+        if (size > 0) {
+            raf.seek(raf.getFilePointer() + size - 1);
+            raf.write(0);
+        }
+        blocks.put(d.offset, d);
+        return d;
+    }
+
+    public DataBlock createDataBlock(byte[] buffer) throws IOException {
+        DataBlock d = new DataBlock(buffer.length);
+        raf.seek(raf.length());
+        d.offset = raf.getFilePointer();
+        d.writeHeader(raf);
+        raf.write(buffer);
+        blocks.put(d.offset, d);
+        return d;
+    }
+
+    public DataBlock createDataBlock(InputStream in) throws IOException {
+        DataBlock d = new DataBlock(0);
+        raf.seek(raf.length());
+        d.offset = raf.getFilePointer();
+        byte[] buffer = new byte[4096];
+        int rd;
+        while ((rd = in.read(buffer)) > 0) {
+            raf.write(buffer, 0, rd);
+            d.length += rd;
+        }
+        raf.seek(d.offset);
+        d.writeHeader(raf);
+        blocks.put(d.offset, d);
+        return d;
+    }
+
+    public byte[] getBytes(DataBlock blk) throws IOException {
+        byte[] buf = new byte[(int)blk.length - 8];
+        raf.seek(blk.offset + 8);
+        raf.read(buf);
+        return buf;
+    }
+
+    public InputStream getInputStream(DataBlock blk) throws IOException {
+        raf.seek(blk.offset + 8);
+        final long end = blk.offset + blk.length;
+        return new InputStream() {
+
+            public int read() throws IOException {
+                if (raf.getFilePointer() < end) {
+                    return raf.read();
+                } else {
+                    return -1;
+                }
+            }
+
+            public int read(byte b[]) throws IOException {
+                return read(b, 0, b.length);
+            }
+
+            public int read(byte b[], int off, int len) throws IOException {
+                if (end - raf.getFilePointer() < len) {
+                    len = (int) (end - raf.getFilePointer());
+                }
+                if (len == 0) {
+                    return -1;
+                }
+                return raf.read(b, off, len);
+            }
+        };
+    }
+
+    public void delete(Block blk) throws IOException {
+        if (blk != null) {
+            for (long o: blk.getOffsets()) {
+                if (o > 0) {
+                    delete(getBlock(o));
+                }
+            }
+            invalidate(blk);
+        }
+    }
+    
+    private void invalidate(Block blk) {
+        InvalidBlock ib = new InvalidBlock(blk.offset, blk.length);
+        ib.modified = true;
+        blocks.put(ib.offset, ib);
+    }
+
+    public void linkBlock(Block block, int id, Block target) throws IOException {
+        Link link = links.get(block.offset + id);
+        if (link == null) {
+            long offset = block.getLinkOffset(id);
+            if (offset != 0) {
+                invalidate(getBlock(offset));
+            }
+            link = new Link(block, target, id);
+        } else {
+            invalidate(link.target);
+        }
+        links.put(link.getKey(), link);
+        block.linkModified(target.offset, id);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <T extends Block> T getLinkedBlock(Block block, int id, Class<T> type, boolean create)
+            throws IOException {
+        Link link = links.get(block.offset + id);
+        if (link == null) {
+            long offset = block.getLinkOffset(id);
+            T blk;
+            if (offset == 0) {
+                if (!create) {
+                    return null;
+                }
+                try {
+                    blk = type.newInstance();
+                } catch (Exception e) {
+                    throw new InternalError(e.toString());
+                }
+            } else {
+                blk = (T) getBlock(offset);
+            }
+            link = new Link(block, blk, id);
+            links.put(link.getKey(), link);
+        }
+        return (T) link.target;
+    }
+
+    public void add(Block block) {
+        added.add(block);
+    }
+
+    public void dump(DumpContext ctx, boolean isLast) {
+        try {
+            open();
+            raf.seek(header.length());
+            Block block = header;
+            while (block != null) {
+                block.dump(ctx, isLast);
+                block = Block.read(raf);
+            }
+        } catch (IOException e) {
+            log.error("Error while accessing file", e);
+        }
+    }
+
+    public void sync() throws IOException {
+        raf.seek(raf.length());
+
+        // first check the linked blocks
+        for (Link link: links.values()) {
+            Block block = link.target;
+            if (block.needsRelocate()) {
+                if (block.offset > 0) {
+                    // invalidate old block
+                    InvalidBlock ib = new InvalidBlock(block.offset, block.length);
+                    ib.modified = true;
+                    blocks.put(block.offset, ib);
+                }
+                block.write(raf);
+                blocks.put(block.offset, block);
+                link.block.linkModified(block.offset, link.id);
+            }
+        }
+
+        // write new blocks
+        for (Block block: added) {
+            block.write(raf);
+            blocks.put(block.offset, block);
+        }
+        added.clear();
+
+        // then write modified
+        for (Block block: blocks.values()) {
+            if (block.isModified()) {
+                raf.seek(block.offset);
+                block.write(raf);
+            }
+        }
+    }
+
+    protected RandomAccessFile openRaf() throws FileNotFoundException {
+        if (raf == null) {
+            raf = new RandomAccessFile(file, "rw");
+        }
+        return raf;
+    }
+
+    public void close() throws IOException {
+        if (raf != null) {
+            sync();
+            try {
+                raf.close();
+            } catch (IOException e) {
+                // ignore
+            }
+            raf = null;
+        }
+        header = null;
+    }
+
+    public static long getLong(byte[] b, int offs) {
+        return ((long)  (b[offs] & 0xFF) << 56) +
+                ((long) (b[1 + offs] & 0xFF) << 48) +
+                ((long) (b[2 + offs] & 0xFF) << 40) +
+                ((long) (b[3 + offs] & 0xFF) << 32) +
+                ((long) (b[4 + offs] & 0xFF) << 24) +
+                ((long) (b[5 + offs] & 0xFF) << 16) +
+                ((long) (b[6 + offs] & 0xFF) << 8) +
+                ((long) (b[7 + offs] & 0xFF));
+    }
+
+    public static void setLong(byte[] b, int offs, long v) {
+        b[offs]   = (byte) ((v >>> 56) & 0xFF);
+        b[offs+1] = (byte) ((v >>> 48) & 0xFF);
+        b[offs+2] = (byte) ((v >>> 40) & 0xFF);
+        b[offs+3] = (byte) ((v >>> 32) & 0xFF);
+        b[offs+4] = (byte) ((v >>> 24) & 0xFF);
+        b[offs+5] = (byte) ((v >>> 16) & 0xFF);
+        b[offs+6] = (byte) ((v >>>  8) & 0xFF);
+        b[offs+7] = (byte) ((v >>>  0) & 0xFF);
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Block.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Block.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Block.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Block.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.Dumpable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>Block</code>...
+ */
+public abstract class Block implements Dumpable {
+
+    /**
+     * default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(Block.class);
+
+    private static final long[] EMPTY_LONG_ARRAY = new long[0];
+
+    public static final byte TYPE_INVALID = 0;
+
+    public static final byte TYPE_HEADER = 1;
+
+    public static final byte TYPE_DATA = 2;
+
+    public static final byte TYPE_INFO = 3;
+
+    public static final byte TYPE_ENTRY = 4;
+
+    public static final byte TYPE_PROPS = 5;
+
+    protected long length;
+
+    protected long offset;
+
+    protected boolean modified;
+
+    protected boolean relocate;
+
+    protected Block() {
+        relocate = true;
+        length = 8;
+    }
+
+    public Block(long offset, long length) {
+        this.offset = offset;
+        this.length = length;
+    }
+
+    public long length() {
+        return length;
+    }
+
+    public long offset() {
+        return offset;
+    }
+
+    public long next() {
+        return offset + length;
+    }
+
+    public static Block read(RandomAccessFile raf) throws IOException {
+        long offset = raf.getFilePointer();
+        if (offset >= raf.length()) {
+            return null;
+        }
+        byte[] tlen = new byte[8];
+        raf.read(tlen);
+        byte type = tlen[0];
+        tlen[0] = 0;
+        long length = BinMetaFile.getLong(tlen, 0);
+        Block blk;
+        switch (type) {
+            case TYPE_INVALID:
+                blk = new InvalidBlock(offset, length);
+                break;
+            case TYPE_DATA:
+                blk = new DataBlock(offset, length);
+                break;
+            case TYPE_INFO:
+                blk = new InfoBlock(offset, length);
+                break;
+            case TYPE_ENTRY:
+                blk = new EntryBlock(offset, length);
+                break;
+            case TYPE_PROPS:
+                blk = new PropertiesBlock(offset, length);
+                break;
+            default:
+                throw new InternalError("invalid block type " + type);
+        }
+        blk.readData(raf);
+        raf.seek(blk.next());
+        return blk;
+    }
+
+    protected void writeHeader(RandomAccessFile raf) throws IOException {
+        byte[] tlen = new byte[8];
+        BinMetaFile.setLong(tlen, 0, length);
+        tlen[0] = getType();
+        raf.write(tlen);
+    }
+
+    public void write(RandomAccessFile raf) throws IOException {
+        offset = raf.getFilePointer();
+        // write (old) header
+        writeHeader(raf);
+        writeData(raf);
+        long length = raf.getFilePointer() - offset;
+
+        // write new header if length is different
+        if (length != this.length) {
+            this.length = length;
+            raf.seek(offset);
+            writeHeader(raf);
+            raf.seek(next());
+        }
+        modified = false;
+        relocate = false;
+    }
+
+    public boolean isModified() {
+        return modified;
+    }
+
+    public boolean needsRelocate() {
+        return relocate;
+    }
+
+    public long getLinkOffset(int id) {
+        return getOffsets()[id];
+    }
+
+    public void linkModified(long targetOffset, int id) {
+        final long[] offsets = getOffsets();
+        if (targetOffset != offsets[id]) {
+            offsets[id] = targetOffset;
+            modified = true;
+        }
+    }
+
+
+    public abstract byte getType();
+
+    public long[] getOffsets() {
+        return EMPTY_LONG_ARRAY;
+    }
+
+    public void readData(RandomAccessFile raf) throws IOException {
+        // do nothing
+    }
+
+    public void writeData(RandomAccessFile raf) throws IOException {
+        // just skip
+        raf.seek(offset + length);
+    }
+
+    public void dump(DumpContext ctx, boolean isLast) {
+        ctx.printf(isLast, "%08x %04x %s%n", offset, length, getClass().getSimpleName());
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/DataBlock.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/DataBlock.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/DataBlock.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/DataBlock.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+/**
+ * <code>EntryBlock</code>...
+ */
+public class DataBlock extends Block {
+
+    public DataBlock() {
+    }
+
+    public DataBlock(long size) {
+        this(0, size + 8);
+    }
+
+    public DataBlock(long offset, long length) {
+        super(offset, length);
+    }
+
+    public byte getType() {
+        return Block.TYPE_DATA;
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/EntryBlock.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/EntryBlock.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/EntryBlock.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/EntryBlock.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+
+/**
+ * <code>EntryBlock</code>...
+ */
+public class EntryBlock extends Block {
+
+    public static final int ID_WORK = 0;
+    public static final int ID_BASE = 1;
+    public static final int ID_MINE = 2;
+    public static final int ID_THEIRS = 3;
+    public static final int ID_TMP = 4;
+
+    private String name = "";
+
+    private String relPath = "";
+
+    private String aggPath = "";
+
+    private long[] offsets = new long[ID_TMP + 1];
+
+    public EntryBlock(String name, String aggPath, String relPath) {
+        this.name = name;
+        this.aggPath = aggPath;
+        this.relPath = relPath;
+    }
+
+    public EntryBlock(long offset, long length) {
+        super(offset, length);
+    }
+
+    public byte getType() {
+        return Block.TYPE_ENTRY;
+    }
+
+    public void readData(RandomAccessFile raf) throws IOException {
+        name = raf.readUTF();
+        aggPath = raf.readUTF();
+        relPath = raf.readUTF();
+        for (int i=0; i<offsets.length; i++) {
+            offsets[i] = raf.readLong();
+        }
+    }
+
+    public void writeData(RandomAccessFile raf) throws IOException {
+        raf.writeUTF(name);
+        raf.writeUTF(aggPath);
+        raf.writeUTF(relPath);
+        for (long off : offsets) {
+            raf.writeLong(off);
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+        relocate = true;
+    }
+
+    public String getRelPath() {
+        return relPath;
+    }
+
+    public void setRelPath(String relPath) {
+        this.relPath = relPath;
+        relocate = true;
+    }
+
+    public String getAggregatePath() {
+        return aggPath;
+    }
+
+    public void setAggregatePath(String aggPath) {
+        this.aggPath = aggPath;
+        relocate = true;
+    }
+
+    public long[] getOffsets() {
+        return offsets;
+    }
+
+    @Override
+    public void dump(DumpContext ctx, boolean isLast) {
+        ctx.printf(false, "  name=%s%n", name);
+        ctx.printf(false, "  work=%08x%n", offsets[ID_WORK]);
+        ctx.printf(false, "  base=%08x%n", offsets[ID_BASE]);
+        ctx.printf(false, "  mine=%08x%n", offsets[ID_MINE]);
+        ctx.printf(false, "  thrs=%08x%n", offsets[ID_THEIRS]);
+        ctx.printf(true, "  temp=%08x%n", offsets[ID_TMP]);
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/HeaderBlock.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/HeaderBlock.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/HeaderBlock.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/HeaderBlock.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+
+/**
+ * <code>HeaderBlock</code>
+ */
+public class HeaderBlock extends Block {
+
+    private static final byte[] MAGIC = new byte[]{'V', 'L', 'T'};
+
+    private static final byte VERSION_1 = 1;
+
+    private static final int LENGTH = 4 + 8;
+
+    private static final int OFFSET_VERSION = 3;
+
+    private byte version = VERSION_1;
+
+    private long[] offsets = new long[1];
+
+    public static final int ID_PROPERTIES = 0;
+
+    public HeaderBlock() {
+        super(0, LENGTH);
+    }
+
+    public byte getVersion() {
+        return version;
+    }
+
+    public void setVersion(byte version) {
+        this.version = version;
+        modified = true;
+    }
+
+    public long[] getOffsets() {
+        return offsets;
+    }
+
+    public void readData(RandomAccessFile raf) throws IOException {
+        byte[] data = new byte[4];
+        raf.read(data);
+        if (data[0] != MAGIC[0] || data[1] != MAGIC[1] || data[2] != MAGIC[2]) {
+            throw new IllegalArgumentException("invalid magic " + new String(data, 0, 3));
+        }
+        version = data[OFFSET_VERSION];
+        if (version > VERSION_1) {
+            throw new IllegalArgumentException("unsupported version " + version);
+        }
+        offsets[0] = raf.readLong();
+    }
+
+    public void write(RandomAccessFile raf) throws IOException {
+        raf.seek(0);
+        raf.write(MAGIC);
+        raf.writeByte(version);
+        raf.writeLong(offsets[0]);
+        modified = false;
+    }
+
+    public byte getType() {
+        return Block.TYPE_HEADER;
+    }
+
+    @Override
+    public void dump(DumpContext ctx, boolean isLast) {
+        ctx.printf(false, "HeaderBlock%n");
+        ctx.printf(false, "  version=%d%n", version);
+        ctx.printf(false, "  propOff=%08x%n", offsets[0]);
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InfoBlock.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InfoBlock.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InfoBlock.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InfoBlock.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.util.MD5;
+
+/**
+ * <code>InfoBlock</code>...
+ */
+public class InfoBlock extends Block {
+
+    public static final int ID_DATA = 0;
+
+    public static final int ID_PROPS = 1;
+
+    private long size;
+
+    private long date;
+
+    private long[] offsets = new long[ID_PROPS + 1];
+
+    private long md5Msb;
+
+    private long md5Lsb;
+
+    private String contentType = "";
+
+    public InfoBlock() {
+    }
+
+    public InfoBlock(long offset, long length) {
+        super(offset, length);
+    }
+
+    public byte getType() {
+        return Block.TYPE_INFO;
+    }
+
+    public void readData(RandomAccessFile raf) throws IOException {
+        size = raf.readLong();
+        date = raf.readLong();
+        for (int i=0; i<offsets.length; i++) {
+            offsets[i] = raf.readLong();
+        }
+        md5Msb = raf.readLong();
+        md5Lsb = raf.readLong();
+        contentType = raf.readUTF();
+    }
+
+    public void writeData(RandomAccessFile raf) throws IOException {
+        raf.writeLong(size);
+        raf.writeLong(date);
+        for (long off : offsets) {
+            raf.writeLong(off);
+        }
+        raf.writeLong(md5Msb);
+        raf.writeLong(md5Lsb);
+        raf.writeUTF(contentType);
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public void setSize(long size) {
+        if (size != this.size) {
+            this.size = size;
+            modified = true;
+        }
+    }
+
+    public long getDate() {
+        return date;
+    }
+
+    public void setDate(long date) {
+        if (this.date != date) {
+            this.date = date;
+            modified = true;
+        }
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        if (!contentType.equals(this.contentType)) {
+            this.contentType = contentType;
+            relocate = true;
+        }
+    }
+
+    public MD5 getMd5() {
+        return new MD5(md5Msb, md5Lsb);
+    }
+
+    public void setMd5(MD5 md5) {
+        if (md5Msb != md5.getMsb() || md5Lsb != md5.getLsb()) {
+            md5Msb = md5.getMsb();
+            md5Lsb = md5.getLsb();
+            modified = true;
+        }
+    }
+
+    public long[] getOffsets() {
+        return offsets;
+    }
+
+    @Override
+    public void dump(DumpContext ctx, boolean isLast) {
+        super.dump(ctx, isLast);
+        ctx.printf(false, "  size=%d%n", size);
+        ctx.printf(false, "  date=%d%n", date);
+        ctx.printf(false, "  data=%08x%n", offsets[ID_DATA]);
+        ctx.printf(false, "   md5=%s%n", getMd5().toString());
+        ctx.printf(false, "  type=%s%n", contentType);
+        ctx.printf(true, "  props=%08x%n", offsets[ID_PROPS]);
+    }
+    
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InvalidBlock.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InvalidBlock.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InvalidBlock.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/InvalidBlock.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+/**
+ * <code>EntryBlock</code>...
+ */
+public class InvalidBlock extends Block {
+
+    public InvalidBlock(long offset, long length) {
+        super(offset, length);
+    }
+
+    public byte getType() {
+        return Block.TYPE_INVALID;
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Link.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Link.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Link.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/Link.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+/**
+ * <code>Link</code>...
+ */
+public class Link {
+
+    protected final Block block;
+
+    protected final Block target;
+
+    protected final int id;
+
+    public Link(Block block, Block target, int id) {
+        this.block = block;
+        this.target = target;
+        this.id = id;
+    }
+    
+    public long getKey() {
+        return block.offset + id;
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/PropertiesBlock.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/PropertiesBlock.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/PropertiesBlock.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/bin/PropertiesBlock.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.bin;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+
+/**
+ * <code>InfoBlock</code>...
+ */
+public class PropertiesBlock extends Block {
+
+    private Map<String, String> props = new HashMap<String, String>();
+
+    public PropertiesBlock() {
+    }
+
+    public PropertiesBlock(long offset, long length) {
+        super(offset, length);
+    }
+
+    public byte getType() {
+        return Block.TYPE_PROPS;
+    }
+
+    public void readData(RandomAccessFile raf) throws IOException {
+        int size = raf.readInt();
+        for (int i=0; i<size; i++) {
+            String name = raf.readUTF();
+            String v = raf.readUTF();
+            props.put(name, v);
+        }
+    }
+
+    public void writeData(RandomAccessFile raf) throws IOException {
+        raf.writeInt(props.size());
+        for (Map.Entry<String, String> e: props.entrySet()) {
+            raf.writeUTF(e.getKey());
+            raf.writeUTF(e.getValue());
+        }
+    }
+
+    public String getProperty(String name) {
+        return props.get(name);
+    }
+
+    public void setProperty(String name, String value) {
+        if (value == null) {
+            relocate = props.remove(name) != null;
+        } else {
+            relocate = !value.equals(props.put(name, value));
+        }
+    }
+
+    @Override
+    public void dump(DumpContext ctx, boolean isLast) {
+        super.dump(ctx, isLast);
+        for (Map.Entry<String, String> e: props.entrySet()) {
+            ctx.printf(false, "  %s=%s%n", e.getKey(), e.getValue());
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntries.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntries.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntries.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.vault.vlt.meta.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
+import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
+import org.apache.jackrabbit.vault.vlt.VltException;
+import org.apache.jackrabbit.vault.vlt.VltFile;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntries;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntry;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * <code>Entries</code>...
+ *
+ */
+public class XmlEntries implements VltEntries {
+
+    public static final String EN_ENTRIES = "entries";
+
+    public static final String AN_PATH = "path";
+
+    public static final String AN_AGGREGATE_PATH = "aggregatePath";
+
+    private final String path;
+
+    private Map<String, VltEntry> entries = new HashMap<String, VltEntry>();
+
+    private boolean dirty;
+
+    public XmlEntries(String path) {
+        this.path = path;
+    }
+
+    public XmlEntries(String path, boolean dirty) {
+        this.path = path;
+        this.dirty = dirty;
+    }
+
+    public String getPath() {
+        return path;
+    }
+    
+    public static XmlEntries load(InputStream in) throws VltException {
+        InputSource source = new InputSource(in);
+        return load(source);
+    }
+
+    public static XmlEntries load(InputSource source) throws VltException {
+        try {
+            DocumentBuilderFactory factory =
+                DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            Document document = builder.parse(source);
+            Element doc = document.getDocumentElement();
+            if (!doc.getNodeName().equals(EN_ENTRIES)) {
+                throw new VltException(source.getSystemId(), "<entries> expected.");
+            }
+            // get uri
+            String path = doc.getAttribute(AN_PATH);
+            XmlEntries entries = new XmlEntries(path);
+
+            // get entries
+            NodeList nodes = doc.getChildNodes();
+            for (int i=0; i<nodes.getLength(); i++) {
+                Node node = nodes.item(i);
+                if (node instanceof Element) {
+                    Element elem = (Element) node;
+                    if (elem.getNodeName().equals(XmlEntry.EN_ENTRY)) {
+                        XmlEntry entry = XmlEntry.load(elem);
+                        entries.entries.put(entry.getName(), entry);
+                    } else {
+                        throw new VltException(source.getSystemId(),
+                                "<entry> expected in <entries> element.");
+                    }
+                }
+            }
+            entries.dirty = false;
+            return entries;
+        } catch (ParserConfigurationException e) {
+            throw new VltException(source.getSystemId(),
+                    "Unable to create configuration XML parser", e);
+        } catch (SAXException e) {
+            throw new VltException(source.getSystemId(),
+                    "Configuration file syntax error.", e);
+        } catch (IOException e) {
+            throw new VltException(source.getSystemId(),
+                    "Configuration file could not be read.", e);
+        }
+    }
+
+    /*
+    public void save(File file) throws VltException {
+        if (file.exists() && !isDirty()) {
+            return;
+        }
+        try {
+            FileOutputStream out = new FileOutputStream(file);
+            save(out);
+            out.close();
+        } catch (IOException e) {
+            throw new VltException(file.getPath(), "Error while saving", e);
+        }
+    }
+    */
+
+    public void save(OutputStream out) throws IOException {
+        OutputFormat fmt = new OutputFormat("xml", "UTF-8", true);
+        fmt.setLineWidth(0);
+        fmt.setIndent(2);
+        XMLSerializer ser = new XMLSerializer(out, fmt);
+        try {
+            write(ser);
+        } catch (SAXException e) {
+            throw new IOException(e.toString());
+        }
+    }
+
+    private void write(ContentHandler handler) throws SAXException {
+        handler.startDocument();
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", AN_PATH, "", "CDATA", path);
+        handler.startElement("", EN_ENTRIES, "", attrs);
+        for (VltEntry e: entries.values()) {
+            ((XmlEntry) e).write(handler);
+        }
+        handler.endElement("", EN_ENTRIES, "");
+        handler.endDocument();
+        dirty = false;
+    }
+
+    public void update(VltFile file) {
+        VltEntry e = file.getEntry();
+        if (e == null) {
+            entries.remove(file.getName());
+            dirty = true;
+        } else {
+            putEntry(e);
+        }
+    }
+
+    public void putEntry(VltEntry e) {
+        if (entries.get(e.getName()) != e) {
+            dirty = true;
+            entries.put(e.getName(), e);
+        }
+    }
+
+    public VltEntry getEntry(String localName) {
+        return entries.get(localName);
+    }
+
+    public VltEntry update(String localName, String aggregatePath, String repoRelPath) {
+        XmlEntry e = new XmlEntry(localName, aggregatePath, repoRelPath);
+        VltEntry old = entries.remove(localName);
+        if (old != null) {
+            e.put(old.work());
+            e.put(old.base());
+            e.put(old.mine());
+            e.put(old.theirs());
+        }
+        putEntry(e);
+        return e;
+    }
+
+    public boolean hasEntry(String localName) {
+        return entries.containsKey(localName);
+    }
+
+    public Collection<VltEntry> entries() {
+        return entries.values();
+    }
+
+    public void setDirty(boolean dirty) {
+        this.dirty = dirty;
+    }
+    
+    public boolean isDirty() {
+        if (dirty) {
+            return true;
+        }
+        for (VltEntry e: entries.values()) {
+            if (e.isDirty()) {
+                return dirty = true;
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntry.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntry.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntry.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.vault.util.MimeTypes;
+import org.apache.jackrabbit.vault.vlt.VltException;
+import org.apache.jackrabbit.vault.vlt.meta.MetaFile;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntry;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntryInfo;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * <code>Entry</code>...
+ *
+ */
+public class XmlEntry implements VltEntry {
+
+    public static final String CONFLICT_NAME_BASE = ".base";
+    public static final String CONFLICT_NAME_MINE = ".mine";
+    public static final String CONFLICT_NAME_THEIRS = ".theirs";
+
+    public static final String EN_ENTRY = "entry";
+
+    public static final String AN_NAME = "name";
+
+    public static final String AN_PATH = "rp";
+
+    public static final String AN_AGGREGATE_PATH = "ap";
+
+    private final String name;
+
+    private final String repoRelPath;
+
+    private final String aggregatePath;
+    
+    private boolean dirty;
+
+    private Map<VltEntryInfo.Type, VltEntryInfo> infos =
+            new EnumMap<VltEntryInfo.Type, VltEntryInfo>(VltEntryInfo.Type.class);
+
+    protected XmlEntry(String name, String aggPath, String repoRelPath) {
+        this.name = name;
+        this.aggregatePath = aggPath;
+        this.repoRelPath = repoRelPath;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getRepoRelPath() {
+        return repoRelPath;
+    }
+
+    public String getAggregatePath() {
+        return aggregatePath;
+    }
+
+    public VltEntryInfo create(VltEntryInfo.Type type) {
+        return new XmlEntryInfo(type);
+    }
+
+    public void put(VltEntryInfo info) {
+        if (info != null) {
+            dirty = true;
+            infos.put(info.getType(), info);
+        }
+    }
+
+    public VltEntryInfo work() {
+        return infos.get(VltEntryInfo.Type.WORK);
+    }
+
+    public VltEntryInfo base() {
+        return infos.get(VltEntryInfo.Type.BASE);
+    }
+
+    public VltEntryInfo mine() {
+        return infos.get(VltEntryInfo.Type.MINE);
+    }
+
+    public VltEntryInfo theirs() {
+        return infos.get(VltEntryInfo.Type.THEIRS);
+    }
+
+    public VltEntryInfo remove(VltEntryInfo.Type type) {
+        dirty = true;
+        return infos.remove(type);
+    }
+
+    public State getState() {
+        if (infos.containsKey(VltEntryInfo.Type.MINE)) {
+            return State.CONFLICT;
+        }
+        if (!infos.containsKey(VltEntryInfo.Type.BASE)) {
+            return State.ADDED;
+        }
+        if (!infos.containsKey(VltEntryInfo.Type.WORK)) {
+            return State.DELETED;
+        }
+        return State.CLEAN;
+    }
+
+
+    public void resolved(MetaFile fileTmp, File fileWork, MetaFile fileBase)
+            throws IOException {
+        // cleanup files
+        XmlEntryInfo mine = (XmlEntryInfo) mine();
+        XmlEntryInfo base = (XmlEntryInfo) base();
+        XmlEntryInfo theirs = (XmlEntryInfo) theirs();
+        XmlEntryInfo work = (XmlEntryInfo) work();
+
+        // delete the .x files
+        new File(fileWork.getParentFile(), base.getName()).delete();
+        new File(fileWork.getParentFile(), mine.getName()).delete();
+        new File(fileWork.getParentFile(), theirs.getName()).delete();
+
+        // copy the tmp file to the base
+        File tmp = fileBase.openTempFile();
+        fileTmp.copyTo(tmp, true);
+        fileBase.closeTempFile(false);
+
+        // and update the infos
+        base.update(theirs);
+        base.setName(null);
+        work.update(fileWork, true);
+        remove(mine.getType());
+        remove(theirs.getType());
+    }
+
+    public boolean delete(File fileWork) {
+        // cleanup files
+        XmlEntryInfo mine = (XmlEntryInfo) mine();
+        XmlEntryInfo base = (XmlEntryInfo) base();
+        XmlEntryInfo theirs = (XmlEntryInfo) theirs();
+
+        // delete the .x files
+        if (mine != null) {
+            new File(fileWork.getParentFile(), mine.getName()).delete();
+        }
+        if (theirs != null) {
+            new File(fileWork.getParentFile(), theirs.getName()).delete();
+        }
+        if (base != null && base.getName() != null) {
+            new File(fileWork.getParentFile(), base.getName()).delete();
+        }
+        fileWork.delete();
+        if (base != null) {
+            base.setName(null);
+        }
+        remove(VltEntryInfo.Type.MINE);
+        remove(VltEntryInfo.Type.THEIRS);
+        remove(VltEntryInfo.Type.WORK);
+
+        return infos.isEmpty();
+    }
+
+    public boolean revertConflict(File work) throws IOException {
+        File dir = work.getParentFile();
+
+        XmlEntryInfo mine = (XmlEntryInfo) mine();
+        XmlEntryInfo theirs = (XmlEntryInfo) theirs();
+        XmlEntryInfo base = (XmlEntryInfo) base();
+
+        File fileMine = new File(dir, mine.getName());
+        if (!fileMine.exists()) {
+            return false;
+        }
+        // copy and delete files
+        FileUtils.copyFile(fileMine, work);
+        fileMine.delete();
+        new File(dir, theirs.getName()).delete();
+        new File(dir, base.getName()).delete();
+
+        // remove infos
+        remove(mine.getType());
+        remove(theirs.getType());
+        base.setName(null);
+
+        // hack: fix content type if it was lost
+        if (mine.getContentType() == null && base.getContentType() == null) {
+            VltEntryInfo workInfo = work();
+            if (workInfo.getContentType() == null) {
+                workInfo.setContentType(MimeTypes.getMimeType(work.getName()));
+            }
+            base.setContentType(workInfo.getContentType());
+        }
+        return true;
+    }
+
+    public void conflict(File work, MetaFile base, MetaFile tmp)
+            throws IOException {
+        File dir = work.getParentFile();
+        File fileMine = new File(dir, name + CONFLICT_NAME_MINE);
+        File fileBase = new File(dir, name + CONFLICT_NAME_BASE);
+        File fileTheirs = new File(dir, name + CONFLICT_NAME_THEIRS);
+
+        // copy the 3 files
+        FileUtils.copyFile(work, fileMine);
+        base.copyTo(fileBase, true);
+        tmp.copyTo(fileTheirs, true);
+
+        // the base gets and additional name
+        ((XmlEntryInfo) base()).setName(fileBase.getName());
+
+        // the 'work' becomes the 'mine'
+        String contentType = base().getContentType();
+        XmlEntryInfo mine = new XmlEntryInfo(VltEntryInfo.Type.MINE);
+        mine.update(fileMine, true);
+        mine.setName(fileMine.getName());
+        mine.setContentType(contentType);
+        put(mine);
+
+        // add the 'theirs' as well
+        XmlEntryInfo theirs = new XmlEntryInfo(VltEntryInfo.Type.THEIRS);
+        theirs.update(fileTheirs, true);
+        theirs.setName(fileTheirs.getName());
+        theirs.setContentType(contentType);
+        put(theirs);
+    }
+
+    public void write(ContentHandler handler) throws SAXException {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", AN_NAME, "", "CDATA", name);
+        if (repoRelPath != null) {
+            attrs.addAttribute("", AN_PATH, "", "CDATA", repoRelPath);
+        }
+        if (aggregatePath != null) {
+            attrs.addAttribute("", AN_AGGREGATE_PATH, "", "CDATA", aggregatePath);
+        }
+        handler.startElement("", EN_ENTRY, "", attrs);
+        for (VltEntryInfo info: infos.values()) {
+            ((XmlEntryInfo) info).write(handler);
+        }
+        handler.endElement("", EN_ENTRY, "");
+        dirty = false;
+    }
+
+    public boolean isDirty() {
+        if (dirty) {
+            return true;
+        }
+        for (VltEntryInfo info: infos.values()) {
+            if (((XmlEntryInfo) info).isDirty()) {
+                return dirty = true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isDirectory() {
+        VltEntryInfo base = infos.get(VltEntryInfo.Type.BASE);
+        if (base != null) {
+            return base.isDirectory();
+        }
+        VltEntryInfo work = infos.get(VltEntryInfo.Type.WORK);
+        return work != null && work.isDirectory();
+    }
+
+    protected static XmlEntry load(Element elem)
+            throws VltException {
+        assert elem.getNodeName().equals(EN_ENTRY);
+        String name = elem.getAttribute(AN_NAME);
+        if (name == null) {
+            throw new VltException("entry has no '" + AN_NAME + "' attribute");
+        }
+        String path = elem.hasAttribute(AN_PATH) ? elem.getAttribute(AN_PATH) : null;
+        String ap = elem.hasAttribute(AN_AGGREGATE_PATH) ? elem.getAttribute(AN_AGGREGATE_PATH) : null;
+
+        XmlEntry entry = new XmlEntry(name, ap, path);
+
+        // add infos
+        NodeList nodes = elem.getChildNodes();
+        for (int i=0; i<nodes.getLength(); i++) {
+            Node node = nodes.item(i);
+            if (node instanceof Element) {
+                entry.put(XmlEntryInfo.load((Element) node));
+            }
+        }
+        entry.dirty = false;
+        return entry;
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntryInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntryInfo.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntryInfo.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntryInfo.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.vlt.meta.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Calendar;
+
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.apache.jackrabbit.vault.util.MD5;
+import org.apache.jackrabbit.vault.vlt.VltException;
+import org.apache.jackrabbit.vault.vlt.meta.MetaFile;
+import org.apache.jackrabbit.vault.vlt.meta.VltEntryInfo;
+import org.w3c.dom.Element;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * <code>Entry</code>...
+ *
+ */
+public class XmlEntryInfo implements VltEntryInfo {
+
+    private static final String AN_NAME = "name";
+    private static final String AN_DATE = "date";
+    private static final String AN_MD5 = "md5";
+    private static final String AN_CONTENT_TYPE = "contentType";
+    private static final String AN_SIZE = "size";
+
+    private final Type type;
+
+    private String name;
+
+    private long date;
+
+    private MD5 md5;
+
+    private String contentType;
+
+    private long size;
+
+    private boolean dirty;
+
+    public XmlEntryInfo(Type type) {
+        this.type = type;
+    }
+
+    public VltEntryInfo copyAs(Type type) {
+        XmlEntryInfo info = new XmlEntryInfo(type);
+        info.update(this);
+        return info;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        if (this.name == null && name != null
+                || this.name != null && !this.name.equals(name)) {
+            this.name = name;
+            dirty = true;
+        }
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public long getDate() {
+        return date;
+    }
+
+    public void setDate(long date) {
+        // round to second
+        date -= date % 1000;
+        if (date != this.date) {
+            this.date = date;
+            dirty = true;
+        }
+    }
+
+    public MD5 getMd5() {
+        return md5;
+    }
+
+    public void setMd5(MD5 md5) {
+        if (this.md5 == null && md5 != null
+                || this.md5 != null && !this.md5.equals(md5)) {
+            this.md5 = md5;
+            dirty = true;
+        }
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        if (this.contentType == null && contentType != null
+                || this.contentType != null && !this.contentType.equals(contentType)) {
+            this.contentType = contentType;
+            dirty = true;
+        }
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public void setSize(long size) {
+        if (size != this.size) {
+            this.size = size;
+            dirty = true;
+        }
+    }
+
+    /**
+     * Checks if the remote file is modified compared to this entry.
+     * It is modified if:
+     * - the local time is 0
+     * - the remote time is 0 or greater than this time.
+     * - the size differs
+     * - the content type differs
+     *
+     * @param remoteFile the remote file to compare with
+     * @return <code>true</code> if modified.
+     */
+    public boolean checkModified(VaultFile remoteFile) {
+        long rTime = remoteFile.lastModified();
+        rTime -= rTime % 1000;
+        
+        if (date <=0 || rTime <= 0 || rTime > date) {
+            return true;
+        }
+        long rSize = remoteFile.length();
+        if (rSize < 0 || rSize != size) {
+            return true;
+        }
+        String ct = remoteFile.getContentType();
+        return ct == null || !ct.equals(contentType);
+    }
+
+
+    public void update(VltEntryInfo base) {
+        setName(((XmlEntryInfo) base).getName());
+        setDate(base.getDate());
+        setMd5(base.getMd5());
+        setContentType(base.getContentType());
+        setSize(base.getSize());
+    }
+
+    public void update(File file, boolean force) throws IOException {
+        if (file.isDirectory()) {
+            dirty = size != 0 && md5 != null && contentType != null;
+            if (dirty) {
+                size = 0;
+                md5 = null;
+                contentType = null;
+                date = file.lastModified();
+            }
+        } else {
+            if (force || file.lastModified() != date || file.length() != size) {
+                size = file.length();
+                md5 = MD5.digest(file);
+                date = file.lastModified();
+                dirty = true;
+            }
+        }
+    }
+
+    public void update(MetaFile file, boolean force) throws IOException {
+        if (force || file.lastModified() > date) {
+            size = file.length();
+            md5 = file.md5();
+            date = file.lastModified();
+            dirty = true;
+        }
+    }
+
+    public boolean isDirectory() {
+        return size == 0 && md5 == null && contentType == null;
+    }
+
+    public boolean isDirty() {
+        return dirty;
+    }
+
+    public boolean isSame(VltEntryInfo base) {
+        return size == base.getSize()
+                && ((md5 == null && base.getMd5() == null)
+                    || md5 != null && md5.equals(base.getMd5()));
+    }
+
+    public void write(ContentHandler handler) throws SAXException {
+        AttributesImpl attrs = new AttributesImpl();
+        addAttributes(attrs);
+        handler.startElement("", type.name().toLowerCase(), "", attrs);
+        handler.endElement("", type.name().toLowerCase(), "");
+        dirty = false;
+    }
+
+    protected void addAttributes(AttributesImpl attrs) {
+        if (name != null) {
+            attrs.addAttribute("", AN_NAME, "", "CDATA", name);
+        }
+        if (date > 0) {
+            Calendar c = Calendar.getInstance();
+            c.setTimeInMillis(date);
+            attrs.addAttribute("", AN_DATE, "", "CDATA", ISO8601.format(c));
+        }
+        if (md5 != null) {
+            attrs.addAttribute("", AN_MD5, "", "CDATA", md5.toString());
+        }
+        if (contentType != null) {
+            attrs.addAttribute("", AN_CONTENT_TYPE, "", "CDATA", contentType);
+        }
+        if (size > 0) {
+            attrs.addAttribute("", AN_SIZE, "", "CDATA", String.valueOf(size));
+        }
+    }
+
+    protected static VltEntryInfo load(Element elem)
+            throws VltException {
+        Type type;
+        try {
+            type = Type.valueOf(elem.getNodeName().toUpperCase());
+        } catch (IllegalArgumentException e) {
+            throw new VltException("unknown entry type '" + elem.getNodeName() + "'");
+        }
+        XmlEntryInfo entry = new XmlEntryInfo(type);
+        if (elem.hasAttribute(AN_NAME)) {
+            entry.setName(elem.getAttribute(AN_NAME));
+        }
+        if (elem.hasAttribute(AN_DATE)) {
+            entry.setDate(ISO8601.parse(elem.getAttribute(AN_DATE)).getTime().getTime());
+        }
+        if (elem.hasAttribute(AN_MD5)) {
+            try {
+                entry.setMd5(new MD5(elem.getAttribute(AN_MD5)));
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+        if (elem.hasAttribute(AN_CONTENT_TYPE)) {
+            entry.setContentType(elem.getAttribute(AN_CONTENT_TYPE));
+        }
+        if (elem.hasAttribute(AN_SIZE)) {
+            try {
+                entry.setSize(Integer.parseInt(elem.getAttribute(AN_SIZE)));
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+        entry.dirty = false;
+        return entry;
+    }
+
+
+}
\ No newline at end of file