blob: 68e07a03c5f49a378dae3e3b4c863655d281b670 [file] [log] [blame]
/*
* 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;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.vault.fs.VaultFileCopy;
import org.apache.jackrabbit.vault.fs.api.VaultFile;
import org.apache.jackrabbit.vault.util.Constants;
import org.apache.jackrabbit.vault.util.LineOutputStream;
import org.apache.jackrabbit.vault.util.MD5;
import org.apache.jackrabbit.vault.util.MimeTypes;
import org.apache.jackrabbit.vault.util.PathUtil;
import org.apache.jackrabbit.vault.util.diff.DiffWriter;
import org.apache.jackrabbit.vault.util.diff.Document;
import org.apache.jackrabbit.vault.util.diff.DocumentDiff;
import org.apache.jackrabbit.vault.util.diff.DocumentDiff3;
import org.apache.jackrabbit.vault.util.diff.DocumentSource;
import org.apache.jackrabbit.vault.util.diff.FileDocumentSource;
import org.apache.jackrabbit.vault.util.diff.Hunk3;
import org.apache.jackrabbit.vault.util.diff.LineElementsFactory;
import org.apache.jackrabbit.vault.vlt.meta.MetaFile;
import org.apache.jackrabbit.vault.vlt.meta.MetaFileDocSource;
import org.apache.jackrabbit.vault.vlt.meta.VltEntry;
import org.apache.jackrabbit.vault.vlt.meta.VltEntryInfo;
/**
* {@code VltFile}...
*
*/
public class VltFile implements DocumentSource {
public static final String PROP_CONTENT_TYPE = "vlt:mime-type";
/**
* Possible state of this file
*/
public enum State {
CLEAN (" "),
ADDED ("A"),
CONFLICTED ("C"),
DELETED ("D"),
IGNORED ("I"),
MODIFIED ("M"),
REPLACED ("R"),
UNKNOWN("?"),
MISSING ("!"),
OBSTRUCTED ("~"),
VOID (" ");
public final String letter;
private State(String letter) {
this.letter = letter;
}
public String toString() {
return name().toLowerCase() + " (" + letter + ")";
}
}
private final VltDirectory parent;
private final File file;
private final String name;
private VltEntry entry;
public VltFile(VltDirectory parent, String name, VltEntry entry)
throws VltException {
this.parent = parent;
if (entry == null) {
// potentially modify the name
name = parent.escapeToMakeUnique(name);
}
this.name = name;
this.entry = entry;
this.file = new File(parent.getDirectory(), name);
}
public Properties getProperties() throws VltException {
Properties props = new Properties();
if (entry != null) {
VltEntryInfo info = entry.work();
String ct = info.getContentType();
if (ct != null) {
props.put(PROP_CONTENT_TYPE, ct);
}
}
return props;
}
public String getProperty(String name) throws VltException {
if (entry != null) {
VltEntryInfo info = entry.work();
if (name.equals(PROP_CONTENT_TYPE)) {
return info.getContentType();
}
}
return null;
}
public void setProperty(String name, String value) throws VltException {
if (entry == null) {
throw error("Can't set property to non controlled file.");
}
VltEntryInfo info = entry.work();
if (info == null) {
throw error("Can't set property to non controlled file.");
}
if (name.equals(PROP_CONTENT_TYPE)) {
if (!file.isDirectory()) {
// silently ignore directories
info.setContentType(value);
parent.getContext().printMessage(this, name + "=" + value);
}
} else {
throw error("Generic properies not supported, yet");
}
}
public State getStatus() throws VltException {
State state = State.VOID;
if (entry == null) {
if (file.exists()) {
// special check for jcr_root
if (file.equals(parent.getContext().getExportRoot().getJcrRoot())) {
state = State.CLEAN;
} else {
state = State.UNKNOWN;
}
} else {
state = State.VOID;
}
} else {
switch (entry.getState()) {
case CLEAN:
if (file.exists()) {
if (file.isDirectory()) {
VltDirectory dir = descend();
if (dir.isControlled()) {
state = State.CLEAN;
} else {
state = State.OBSTRUCTED;
}
} else {
VltEntryInfo work = entry.work();
VltEntryInfo base = entry.base();
assert work != null;
assert base != null;
try {
work.update(file, false);
} catch (IOException e) {
throw exception("Error while calculating status.", e);
}
state = work.isSame(base) ? State.CLEAN : State.MODIFIED;
}
} else {
state = State.MISSING;
}
break;
case ADDED:
if (file.exists()) {
state = State.ADDED;
} else {
state = State.MISSING;
}
break;
case CONFLICT:
state = State.CONFLICTED;
break;
case DELETED:
state = State.DELETED;
break;
}
}
return state;
}
public String getName() {
return name;
}
public File getFile() {
return file;
}
public String getPath() {
return file.getPath();
}
public MetaFile getBaseFile(boolean create) throws VltException {
try {
return parent.getMetaDirectory().getBaseFile(name, create);
} catch (IOException e) {
throw new VltException(getPath(), "Error opening base file.", e);
}
}
public String getContentType() {
if (entry != null && !file.isDirectory()) {
VltEntryInfo work = entry.work();
if (work != null) {
return work.getContentType();
}
}
return null;
}
/**
* Checks if this file has binary content. It does not actually read the
* file data but calls {@link MimeTypes#isBinary(String)} with the content
* type of the work file.
* @return {@code true} if this is binary
*/
public boolean isBinary() {
return MimeTypes.isBinary(getContentType());
}
public MetaFile getTmpFile() throws VltException {
try {
return parent.getMetaDirectory().getTmpFile(name, true);
} catch (IOException e) {
throw new VltException(getPath(), "Error opening tmp file.", e);
}
}
public boolean canDescend() {
return file.isDirectory();
}
public VltDirectory descend() throws VltException {
if (!canDescend()) {
throw new VltException("Cannot descend into non directory.");
}
return new VltDirectory(parent.getContext(), file);
}
public VltEntry getEntry() {
return entry;
}
public void diff() throws VltException {
State state = getStatus();
if (entry == null || entry.isDirectory()) {
return;
}
VltEntryInfo work = entry.work();
VltEntryInfo base = entry.base();
if (work == null || base == null) {
return;
}
switch (state) {
case ADDED:
case CONFLICTED:
case DELETED:
case MODIFIED:
break;
case IGNORED:
case MISSING:
case OBSTRUCTED:
case REPLACED:
case UNKNOWN:
case VOID:
case CLEAN:
return;
}
if (MimeTypes.isBinary(work.getContentType()) || MimeTypes.isBinary(base.getContentType())) {
PrintStream s = parent.getContext().getStdout();
s.printf("Index: %s%n", getName());
s.println("===================================================================");
s.println("Cannot display: file marked as binary type.");
s.printf("vlt:mime-type = %s%n", work.getContentType());
s.flush();
return;
}
try {
// do the actual diff
PrintStream s = parent.getContext().getStdout();
DiffWriter out = new DiffWriter(new OutputStreamWriter(s, Constants.ENCODING));
out.write("Index: ");
out.write(getName());
out.writeNewLine();
out.write("===================================================================");
out.writeNewLine();
Reader r0 = getBaseFile(false) == null ? null : getBaseFile(false).getReader();
Document d0 = new Document(this, LineElementsFactory.create(this, r0, false));
Reader r1 = file.exists() ? new InputStreamReader(FileUtils.openInputStream(file), Constants.ENCODING) : null;
Document d1 = new Document(this, LineElementsFactory.create(this, r1, false));
DocumentDiff diff;
try {
diff = d0.diff(d1);
} finally {
IOUtils.closeQuietly(r0);
IOUtils.closeQuietly(r1);
}
diff.write(out, 3);
out.flush();
} catch (IOException e) {
throw exception("Error while writing diff.", e);
}
}
public FileAction delete(boolean force) throws VltException {
State state = getStatus();
switch (state) {
case ADDED:
case CONFLICTED:
case MODIFIED:
case REPLACED:
if (!force) {
parent.getContext().printMessage(this, "has local modification. use --force to delete anyway");
return FileAction.VOID;
}
break;
case CLEAN:
case MISSING:
case DELETED:
break;
case IGNORED:
case OBSTRUCTED:
case UNKNOWN:
case VOID:
if (!force) {
parent.getContext().printMessage(this, "is not under version control. use --force to delete anyway");
return FileAction.VOID;
}
break;
}
if (entry != null && entry.delete(file)) {
entry = null;
}
return FileAction.DELETED;
}
public FileAction commit(VaultFile remoteFile) throws VltException {
if (remoteFile == null) {
return doDelete(false);
} else {
return doUpdate(remoteFile, false);
}
}
public boolean revert() throws VltException {
State state = getStatus();
switch (state) {
case ADDED:
doDelete(true);
entry = null;
return true;
case CONFLICTED:
resolved(true);
// no break;
case DELETED:
case MISSING:
case MODIFIED:
doRevert();
return true;
case IGNORED:
case CLEAN:
case OBSTRUCTED:
case REPLACED:
case UNKNOWN:
case VOID:
default:
return false;
}
}
public boolean resolved(boolean force) throws VltException {
if (getStatus() != State.CONFLICTED) {
return false;
}
if (!force) {
// check if the file still contains the diff markers
boolean mayContainMarker = false;
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
String line;
while ((line = in.readLine()) != null) {
if (line.startsWith(Hunk3.MARKER_B[0])
|| line.startsWith(Hunk3.MARKER_L[0])
|| line.startsWith(Hunk3.MARKER_R[0])
|| line.startsWith(Hunk3.MARKER_M[0])) {
mayContainMarker = true;
break;
}
}
} catch (IOException e) {
throw exception("Error while reading file.", e);
}
if (mayContainMarker) {
throw error("File still contains conflict markers. use --force to force resolve.");
}
}
// resolve entry
try {
entry.resolved(getTmpFile(), file, getBaseFile(false));
} catch (IOException e) {
throw exception("Error while copying files.", e);
}
return true;
}
public FileAction update(VaultFile remoteFile, boolean force)
throws VltException {
State state = getStatus();
switch (state) {
case IGNORED:
case OBSTRUCTED:
case REPLACED:
if (!force || remoteFile == null) {
throw error("update not possible. file is " + state.name().toLowerCase() + ". " +
"Specify --force to overwrite existing files.");
}
return doUpdate(remoteFile, false);
case ADDED:
if (remoteFile != null) {
if (mergeableWithRemote(remoteFile) != FileAction.VOID) {
throw error("Failed to add file: object of the same name already exists.");
}
return doUpdate(remoteFile, false);
} else {
return FileAction.VOID;
}
case CLEAN:
if (remoteFile == null) {
return doDelete(false);
} else {
if (file.isDirectory()) {
// do nothing
return FileAction.VOID;
} else {
return doUpdate(remoteFile, false);
}
}
case CONFLICTED:
if (remoteFile == null) {
try {
if (!entry.revertConflict(file)) {
return FileAction.CONFLICTED;
}
} catch (IOException e) {
throw exception("Error during update.", e);
}
// refetch status, and delete file if clean
return doDelete(getStatus() != State.CLEAN);
} else {
try {
if (!entry.revertConflict(file)) {
return doMerge(remoteFile, FileAction.CONFLICTED);
} else {
return doMerge(remoteFile, FileAction.UPDATED);
}
} catch (IOException e) {
throw exception("Error during update.", e);
}
}
case DELETED:
if (remoteFile == null) {
// we can delete the entry since someone else deleted it as well
return doDelete(false);
} else {
// just update base and entry, in case someone wants to revert
return doUpdate(remoteFile, true);
}
case MISSING:
if (remoteFile == null) {
// if file is missing, just delete it
return doDelete(false);
} else {
// do update
entry = null;
return doUpdate(remoteFile, false);
}
case MODIFIED:
if (remoteFile == null) {
// keep the file
return doDelete(true);
} else {
return doMerge(remoteFile, FileAction.VOID);
}
case UNKNOWN:
if (remoteFile == null) {
// do nothing
return FileAction.VOID;
} else {
// do update
if (file.exists() && !force) {
throw error("Failed to update: object of the same name already exists." +
" Specify --force to overwrite existing files.");
}
return doUpdate(remoteFile, false);
}
case VOID:
// do update
return doUpdate(remoteFile, false);
default:
throw exception("illegal state: " + state, null);
}
}
public FileAction status(VaultFile remoteFile) throws VltException {
State state = getStatus();
switch (state) {
case IGNORED:
case OBSTRUCTED:
case REPLACED:
return FileAction.CONFLICTED;
case ADDED:
if (remoteFile != null) {
return FileAction.CONFLICTED;
} else {
return FileAction.VOID;
}
case CLEAN:
if (remoteFile == null) {
return FileAction.DELETED;
} else {
if (file.isDirectory()) {
// do nothing
return FileAction.VOID;
} else {
return equalsToRemote(remoteFile)
? FileAction.VOID
: FileAction.UPDATED;
}
}
case CONFLICTED:
// do not probe further
return FileAction.CONFLICTED;
case DELETED:
return FileAction.VOID;
case MISSING:
return FileAction.ADDED;
case MODIFIED:
if (remoteFile == null) {
return FileAction.DELETED;
} else {
return mergeableWithRemote(remoteFile);
}
case UNKNOWN:
if (remoteFile == null) {
// do nothing
return FileAction.VOID;
} else {
return FileAction.UPDATED;
}
case VOID:
return FileAction.ADDED;
default:
throw exception("illegal state: " + state, null);
}
}
public FileAction add(boolean force) throws VltException {
State state = getStatus();
switch (state) {
case ADDED:
case CLEAN:
case CONFLICTED:
case MISSING:
case MODIFIED:
case OBSTRUCTED:
case REPLACED:
parent.getContext().printMessage(this, "is already under version control");
break;
case DELETED:
parent.getContext().printMessage(this, "replace not supported yet");
break;
case IGNORED:
parent.getContext().printMessage(this, "failed to add. is ignored.");
break;
case UNKNOWN:
case VOID:
return doAdd(force);
}
return FileAction.VOID;
}
private FileAction doAdd(boolean force) throws VltException {
assert entry == null;
entry = parent.getEntries().update(getName(), null, null);
VltEntryInfo work = entry.create(VltEntryInfo.Type.WORK);
try {
work.update(file, true);
} catch (IOException e) {
throw exception("Error while adding file", e);
}
String contentType = MimeTypes.getMimeType(file.getName(), MimeTypes.APPLICATION_OCTET_STREAM);
work.setContentType(contentType);
entry.put(work);
return FileAction.ADDED;
}
private FileAction doDelete(boolean keepFile)
throws VltException {
// small hack to remove meta directory. should actually be somewhere else
if (file.isDirectory()) {
VltDirectory dir = new VltDirectory(parent.getContext(), file);
dir.uncontrol();
} else {
try {
if (getBaseFile(false) != null) {
getBaseFile(false).delete();
}
} catch (IOException e) {
throw new VltException(getPath(), "Error while deleting base file.", e);
}
}
if (!keepFile) {
file.delete();
}
entry = null;
return FileAction.DELETED;
}
private FileAction doMerge(VaultFile remoteFile, FileAction action)
throws VltException {
if (remoteFile.isDirectory()) {
throw exception("Error while merging. remote is a directory.", null);
}
// abort merger if actions is already conflict
if (action == FileAction.CONFLICTED) {
return action;
}
MetaFile baseFile = getBaseFile(false);
MetaFile tmpFile = getTmpFile();
VltEntryInfo base = entry.base();
VltEntryInfo work = entry.work();
byte[] lineFeed = MimeTypes.isBinary(remoteFile.getContentType())
? null
: LineOutputStream.LS_NATIVE;
// get the remote file
VaultFileCopy copy = null;
boolean remoteUpdated = true;
try {
// first check size and last modified
if (!base.checkModified(remoteFile)) {
remoteUpdated = false;
} else {
File temp = tmpFile.openTempFile();
copy = VaultFileCopy.copy(remoteFile, temp, lineFeed);
// if tmp is equal to the base one, there was no update on the server
if (copy.getMd5().equals(base.getMd5())) {
tmpFile.closeTempFile(tmpFile.length() >= 0);
remoteUpdated = false;
} else {
tmpFile.closeTempFile(false);
}
}
} catch (IOException e) {
throw exception("Error while copying files.", e);
}
if (!remoteUpdated) {
if (work.getMd5().equals(base.getMd5())) {
// fix base
base.setSize(work.getSize());
base.setDate(work.getDate());
return FileAction.VOID;
} else if (remoteFile.lastModified() > 0) {
// normal modification provided
return action;
}
}
try {
// check if binary
boolean remoteBT = getRemoteBinaryType(remoteFile, copy);
boolean localBT = MimeTypes.isBinary(base.getContentType());
if (remoteBT || localBT) {
parent.getContext().printMessage(this, "can't merge. binary content");
entry.conflict(file, baseFile, tmpFile);
return FileAction.CONFLICTED;
}
// do a 3-way diff between the base, the local and the remote one.
// we currently do not use document sources, since we don't really have
// a label to provide (like rev. num, etc).
Reader r0 = baseFile.getReader();
Reader r1 = tmpFile.getReader();
Document baseDoc = new Document(null, LineElementsFactory.create(new MetaFileDocSource(baseFile), r0, false));
Document leftDoc = new Document(null, LineElementsFactory.create(new FileDocumentSource(file), false, Constants.ENCODING));
Document rightDoc = new Document(null, LineElementsFactory.create(new MetaFileDocSource(tmpFile), r1, false));
DocumentDiff3 diff;
try {
diff = baseDoc.diff3(leftDoc, rightDoc);
} finally {
IOUtils.closeQuietly(r0);
IOUtils.closeQuietly(r1);
}
// save the diff output
Writer out = new OutputStreamWriter(FileUtils.openOutputStream(file), Constants.ENCODING);
try {
diff.write(new DiffWriter(out), false);
} catch (IOException e) {
IOUtils.closeQuietly(out);
}
if (diff.hasConflicts()) {
entry.conflict(file, baseFile, tmpFile);
action = FileAction.CONFLICTED;
} else {
// make the tmp file the new base
tmpFile.moveTo(baseFile);
base.update(baseFile, true);
action = FileAction.MERGED;
}
// and update the 'work'
// check if MD5 changes and change action accordingly
MD5 oldMd5 = work.getMd5();
work.update(file, true);
if (oldMd5.equals(work.getMd5())) {
action = FileAction.VOID;
}
// check if remote file provided a last modified
if (remoteFile.lastModified() == 0) {
if (work.getMd5().equals(base.getMd5())) {
base.setDate(work.getDate());
} else {
base.setDate(System.currentTimeMillis());
}
}
} catch (IOException e) {
throw exception("Error during merge operation.", e);
}
return action;
}
private boolean getRemoteBinaryType(VaultFile remoteFile, VaultFileCopy copy) {
// check if binary
boolean remoteBT = MimeTypes.isBinary(remoteFile.getContentType());
if (copy != null && remoteBT != copy.isBinary()) {
parent.getContext().printMessage(this, "Remote Binary type differs from actual data. Content Type: " + remoteFile.getContentType() + " Data is binary: " + copy.isBinary() + ". Using data type.");
remoteBT = copy.isBinary();
}
return remoteBT;
}
private FileAction mergeableWithRemote(VaultFile remoteFile)
throws VltException {
if (remoteFile.isDirectory() != file.isDirectory()) {
return FileAction.CONFLICTED;
}
if (file.isDirectory()) {
return FileAction.VOID;
}
MetaFile tmpFile = getTmpFile();
VltEntryInfo base = entry.base();
// get the remote file
byte[] lineFeed = MimeTypes.isBinary(remoteFile.getContentType())
? null
: LineOutputStream.LS_NATIVE;
VaultFileCopy copy;
try {
File temp = tmpFile.openTempFile();
copy = VaultFileCopy.copy(remoteFile, temp, lineFeed);
if (base == null) {
tmpFile.closeTempFile(true);
// if base is null, file was only added so check the work entry
VltEntryInfo work = entry.work();
if (copy.getMd5().equals(work.getMd5())) {
return FileAction.VOID;
} else {
return FileAction.CONFLICTED;
}
}
// if tmp is equal to the base one, there was not update on the server
if (copy.getMd5().equals(base.getMd5())) {
tmpFile.closeTempFile(true);
return FileAction.VOID;
}
// keep tmp file
tmpFile.closeTempFile(false);
} catch (IOException e) {
throw exception("Error while copying files.", e);
}
// check if binary
boolean remoteBT = getRemoteBinaryType(remoteFile, copy);
if (remoteBT || MimeTypes.isBinary(base.getContentType())) {
return FileAction.CONFLICTED;
}
try {
// do a 3-way diff between the base, the local and the remote one.
// we currently do not use document sources, since we don't really have
// a label to provide (like rev. num, etc).
MetaFile baseFile = getBaseFile(false);
Reader r0 = baseFile.getReader();
Reader r1 = tmpFile.getReader();
Document baseDoc = new Document(null, LineElementsFactory.create(new MetaFileDocSource(baseFile), r0, false));
Document leftDoc = new Document(null, LineElementsFactory.create(new FileDocumentSource(file), false, Constants.ENCODING));
Document rightDoc = new Document(null, LineElementsFactory.create(new MetaFileDocSource(tmpFile), r1, false));
DocumentDiff3 diff;
try {
diff = baseDoc.diff3(leftDoc, rightDoc);
} finally {
IOUtils.closeQuietly(r0);
IOUtils.closeQuietly(r1);
}
if (diff.hasConflicts()) {
return FileAction.CONFLICTED;
} else {
return FileAction.MERGED;
}
} catch (IOException e) {
throw exception("Error during merge operation.", e);
}
}
private void doRevert() throws VltException {
if (entry.isDirectory()) {
file.mkdir();
} else {
try {
getBaseFile(false).copyTo(getFile(), true);
} catch (IOException e) {
throw exception("Error while copying files.", e);
}
}
VltEntryInfo base = entry.base();
entry.put(base.copyAs(VltEntryInfo.Type.WORK));
}
private boolean equalsToRemote(VaultFile remoteFile)
throws VltException {
MetaFile tmpFile = getTmpFile();
// copy file
byte[] lineFeed = MimeTypes.isBinary(remoteFile.getContentType())
? null
: LineOutputStream.LS_NATIVE;
VaultFileCopy copy;
File temp = null;
try {
temp = tmpFile.openTempFile();
copy = VaultFileCopy.copy(remoteFile, temp, lineFeed);
} catch (IOException e) {
throw exception("Error while copying files.", e);
} finally {
if (tmpFile != null) {
try {
tmpFile.closeTempFile(true);
} catch (IOException e) {
// ignore
}
}
}
// if md5 is equal, no update
VltEntryInfo base = entry.base();
return copy.getMd5().equals(base.getMd5());
}
private FileAction doUpdate(VaultFile remoteFile, boolean baseOnly)
throws VltException {
FileAction action;
VltEntryInfo base;
if (entry == null || entry.base() == null) {
// new entry
action = FileAction.ADDED;
entry = parent.getEntries().update(getName(), remoteFile.getAggregatePath(), remoteFile.getRepoRelPath());
base = entry.create(VltEntryInfo.Type.BASE);
entry.put(base);
} else {
action = FileAction.UPDATED;
base = entry.base();
// quick check if modified
if (!base.checkModified(remoteFile)) {
return FileAction.VOID;
}
}
long lastMod = remoteFile.lastModified();
if (lastMod == 0) {
lastMod = System.currentTimeMillis();
}
base.setDate(lastMod);
if (remoteFile.isDirectory()) {
if (!baseOnly) {
// ensure controlled
// todo: this does not belong here
if (entry.work() != null) {
action = FileAction.VOID;
} else {
entry.put(base.copyAs(VltEntryInfo.Type.WORK));
}
file.mkdir();
file.setLastModified(base.getDate());
VltDirectory dir = new VltDirectory(parent.getContext(), file);
if (!dir.isControlled()) {
dir.control(remoteFile.getPath(), remoteFile.getControllingAggregate().getPath());
action = FileAction.ADDED;
}
}
} else {
MetaFile baseFile = getBaseFile(true);
// copy file
byte[] lineFeed = MimeTypes.isBinary(remoteFile.getContentType())
? null
: LineOutputStream.LS_NATIVE;
VaultFileCopy copy;
try {
File temp = baseFile.openTempFile();
copy = VaultFileCopy.copy(remoteFile, temp, lineFeed);
baseFile.closeTempFile(false);
} catch (IOException e) {
throw exception("Error while copying files.", e);
}
// if md5 is equal, no update
if (copy.getMd5().equals(base.getMd5())) {
action = FileAction.VOID;
}
if (action == FileAction.VOID
&& (base.getContentType() != null || remoteFile.getContentType() != null)
&& (base.getContentType() == null || !base.getContentType().equals(remoteFile.getContentType()))) {
action = FileAction.UPDATED;
}
// update infos
VltEntryInfo work = entry.work();
base.setContentType(remoteFile.getContentType());
base.setSize(copy.getLength());
base.setMd5(copy.getMd5());
if (!baseOnly) {
// only copy if not equal
if (work == null || !work.getMd5().equals(copy.getMd5()) || !getFile().exists()) {
try {
baseFile.copyTo(getFile(), true);
entry.put(base.copyAs(VltEntryInfo.Type.WORK));
} catch (IOException e) {
throw exception("Error while copying files.", e);
}
}
}
}
return action;
}
private VltException exception(String msg, Throwable cause) {
return parent.getContext().exception(getPath(), msg, cause);
}
private VltException error(String msg) {
return parent.getContext().error(getPath(), msg);
}
//-----------------------------------------------------< DocumentSource >---
public String getLabel() {
return getName();
}
public String getLocation() {
File cwd = parent.getContext().getCwd();
return PathUtil.getRelativeFilePath(cwd.getPath(), file.getPath());
}
}