/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed 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 li.strolch.xmlpers.api;
import java.io.File;
import java.text.MessageFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.xmlpers.impl.PathBuilder;
import li.strolch.xmlpers.objref.ObjectRef;
public class FileDao {
private static final Logger logger = LoggerFactory.getLogger(FileDao.class);
private final PersistenceTransaction tx;
private final boolean verbose;
private final PathBuilder pathBuilder;
public FileDao(PersistenceTransaction tx, PathBuilder pathBuilder, boolean verbose) {
this.tx = tx;
this.pathBuilder = pathBuilder;
this.verbose = verbose;
}
private void assertIsIdRef(IoOperation ioOperation, ObjectRef objectRef) {
if (!objectRef.isLeaf()) {
String msg = "A {0} operation can only be performed with IdRefs!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, ioOperation);
throw new XmlPersistenceException(msg);
}
}
public <T> boolean exists(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.READ, objectRef);
File path = objectRef.getPath(this.pathBuilder);
return path.exists();
}
public <T> void performCreate(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.CREATE, objectRef);
File path = objectRef.getPath(this.pathBuilder);
logPath(IoOperation.CREATE, path, objectRef);
assertPathNotExists(path, objectRef);
createMissingParents(path, objectRef);
FileIo fileIo = new FileIo(path);
this.tx.getIoMode().write(ctx, fileIo);
}
public <T> void performRead(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.READ, objectRef);
File path = objectRef.getPath(this.pathBuilder);
if (!path.exists()) {
ctx.setObject(null);
return;
}
logPath(IoOperation.READ, path, objectRef);
FileIo fileIo = new FileIo(path);
this.tx.getIoMode().read(ctx, fileIo);
}
public <T> void performUpdate(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.UPDATE, objectRef);
File path = objectRef.getPath(this.pathBuilder);
logPath(IoOperation.UPDATE, path, objectRef);
assertPathIsFileAndWritable(path, objectRef);
FileIo fileIo = new FileIo(path);
this.tx.getIoMode().write(ctx, fileIo);
}
public <T> void performDelete(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.DELETE, objectRef);
File path = objectRef.getPath(this.pathBuilder);
logPath(IoOperation.DELETE, path, objectRef);
assertPathIsFileAndWritable(path, objectRef);
if (!path.delete()) {
String msg = "Failed to delete file {0}"; //$NON-NLS-1$
throw new RuntimeException(MessageFormat.format(msg, path.getAbsolutePath()));
}
ObjectRef parentRef = objectRef.getParent(this.tx);
deleteEmptyDirectories(parentRef);
}
private void deleteEmptyDirectories(ObjectRef objectRef) {
// root can't be deleted
if (objectRef.isRoot())
return;
if (objectRef.isLeaf()) {
throw new IllegalArgumentException("IdRefs don't reference directories!"); //$NON-NLS-1$
}
objectRef.lock();
try {
File directoryPath = objectRef.getPath(this.pathBuilder);
if (!directoryPath.isDirectory()) {
String msg = "The path for {0} is not a directory: {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), directoryPath.getAbsolutePath());
throw new IllegalArgumentException(msg);
}
// stop if empty
if (directoryPath.list().length != 0)
return;
// delete
if (!directoryPath.delete()) {
String msg = "Deletion of empty directory for {0} at {1} failed! Check file permissions!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), directoryPath.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
// log
if (this.verbose) {
String msg = "Deleted empty directory for {0} at {1}"; //$NON-NLS-1$
logger.info(MessageFormat.format(msg, objectRef.getName(), directoryPath));
}
// recursively delete
ObjectRef parent = objectRef.getParent(this.tx);
deleteEmptyDirectories(parent);
} finally {
objectRef.unlock();
}
}
private void logPath(IoOperation operation, File path, ObjectRef objectRef) {
if (this.verbose) {
String msg = "Path for operation {0} for {1} is at {2}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, operation, objectRef.getName(), path.getAbsolutePath());
logger.info(msg);
}
}
private void createMissingParents(File path, ObjectRef objectRef) {
ObjectRef parentRef = objectRef.getParent(this.tx);
parentRef.lock();
try {
File parentFile = parentRef.getPath(this.pathBuilder);
if (!parentFile.exists() && !parentFile.mkdirs()) {
String msg = "Could not create parent path for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
} finally {
parentRef.unlock();
}
}
private void assertPathIsFileAndWritable(File path, ObjectRef objectRef) {
if (!path.exists()) {
String msg = "Persistence unit does not exist for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
if (!path.isFile() || !path.canWrite()) {
String msg;
msg = "Persistence unit is not a file or is not readable for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
}
private void assertPathNotExists(File path, ObjectRef objectRef) {
if (path.exists()) {
String msg = "Persistence unit already exists for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
}
}