/* * Copyright 2004 - 2009 Christian Sprajc. All rights reserved. * * This file is part of PowerFolder. * * PowerFolder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * PowerFolder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PowerFolder. If not, see <http://www.gnu.org/licenses/>. * * $Id: FileInfo.java 8176 2009-06-10 13:21:06Z bytekeeper $ */ package de.dal33t.powerfolder.light; import java.io.File; import java.io.IOException; import java.io.ObjectInput; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import de.dal33t.powerfolder.disk.Folder; import de.dal33t.powerfolder.util.Base64; import de.dal33t.powerfolder.util.Reject; import de.dal33t.powerfolder.util.os.OSUtil; /** * Factory to create {@link FileInfo} and {@link DirectoryInfo} objects. * * @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc </a> */ public final class FileInfoFactory { private static final Logger LOG = Logger.getLogger(FileInfoFactory.class .getName()); private FileInfoFactory() { // No instance allowed } public static FileInfo readExt(ObjectInput in) throws IOException, ClassNotFoundException { int type = in.readInt(); FileInfo fileInfo = type == 0 ? new FileInfo() : new DirectoryInfo(); fileInfo.readExternal(in); return fileInfo; } /** * @param folder * @param name * @return a ACTUAL FileInfo object use to lookup other FileInfo instances. */ public static FileInfo lookupInstance(FolderInfo folder, String name) { return lookupInstance(folder, name, false); } /** * @param folder * @param name * @return a DirectoryInfo object use to lookup. */ public static DirectoryInfo lookupDirectory(FolderInfo folder, String name) { return (DirectoryInfo) lookupInstance(folder, name, true); } /** * @param folder * @param name * @param dir * @return a FileInfo or DirectoryInfo object use to lookup other File or * DirectoryInfo instances. */ public static FileInfo lookupInstance(FolderInfo folder, String name, boolean dir) { if (dir) { return new DirectoryInfo(folder, name); } return new FileInfo(folder, name); } public static FileInfo lookupInstance(Folder folder, File file) { String fn = buildFileName(folder.getLocalBase(), file); return lookupInstance(folder.getInfo(), fn, file.isDirectory()); } /** * Returns a FileInfo with changed FolderInfo. No version update etc. * whatsoever happens. * * @param original * @param fi * @return the new (or existing) instance. */ public static FileInfo changedFolderInfo(FileInfo original, FolderInfo fi) { Reject.ifNull(original, "Original FileInfo is null"); if (original.isLookupInstance()) { // TODO Check if this causes problems with DirectoryInfo return lookupInstance(fi, original.getRelativeName()); } else { if (original.getFolderInfo().equals(fi)) { return original; } if (original.isFile()) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Corrected FolderInfo on " + original.toDetailString()); } return new FileInfo(original.getRelativeName(), original.getSize(), original.getModifiedBy(), original.getModifiedDate(), original.getVersion(), original.isDeleted(), fi.intern()); } else if (original.isDiretory()) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Corrected DirectoryInfo on " + original.toDetailString()); } return new DirectoryInfo(original.getRelativeName(), original.getSize(), original.getModifiedBy(), original.getModifiedDate(), original.getVersion(), original.isDeleted(), fi.intern()); } else { throw new IllegalArgumentException( "Illegal original FileInfo: " + original.getClass() + ": " + original.toDetailString()); } } } public static FileInfo unmarshallExistingFile(FolderInfo fi, String fileName, long size, MemberInfo modby, Date modDate, int version, boolean dir) { if (dir) { return new DirectoryInfo(fileName, size, modby, modDate, version, false, fi); } return new FileInfo(fileName, size, modby, modDate, version, false, fi); } public static FileInfo unmarshallDeletedFile(FolderInfo fi, String fileName, MemberInfo modby, Date modDate, int version, boolean dir) { if (dir) { return new DirectoryInfo(fileName, 0, modby, modDate, version, true, fi); } return new FileInfo(fileName, 0, modby, modDate, version, true, fi); } /** * Initalize within a folder * * @param folder * @param localFile * @param creator * @param directory * if the given file is a directory. * @return the new file */ public static FileInfo newFile(Folder folder, File localFile, MemberInfo creator, boolean directory) { if (directory) { return new DirectoryInfo(buildFileName(folder.getLocalBase(), localFile), creator, new Date(localFile.lastModified()), 0, false, folder.getInfo()); } else { return new FileInfo( buildFileName(folder.getLocalBase(), localFile), localFile.length(), creator, new Date(localFile.lastModified()), 0, false, folder.getInfo()); } } public static FileInfo modifiedFile(FileInfo original, Folder folder, File localFile, MemberInfo modby) { Reject.ifNull(original, "Original FileInfo is null"); Reject.ifTrue(original.isLookupInstance(), "Cannot modify template FileInfo!"); Reject.ifNull(folder, "Folder is null"); String fn = buildFileName(folder.getLocalBase(), localFile); if (original.getRelativeName().equals(fn)) { fn = original.getRelativeName(); } boolean isDir = localFile.isDirectory(); if (original.isFile()) { if (isDir) { return new DirectoryInfo(fn, localFile.length(), modby, new Date(localFile.lastModified()), original.getVersion() + 1, false, original.getFolderInfo()); } return new FileInfo(fn, localFile.length(), modby, new Date( localFile.lastModified()), original.getVersion() + 1, false, original.getFolderInfo()); } else if (original.isDiretory()) { if (!isDir) { return new FileInfo(fn, localFile.length(), modby, new Date( localFile.lastModified()), original.getVersion() + 1, false, original.getFolderInfo()); } return new DirectoryInfo(fn, localFile.length(), modby, new Date( localFile.lastModified()), original.getVersion() + 1, false, original.getFolderInfo()); } else { throw new IllegalArgumentException("Illegal original FileInfo: " + original.getClass() + ": " + original.toDetailString()); } } public static FileInfo deletedFile(FileInfo original, MemberInfo delby, Date delDate) { Reject.ifNull(original, "Original FileInfo is null"); Reject.ifTrue(original.isLookupInstance(), "Cannot delete template FileInfo!"); if (original.isFile()) { return new FileInfo(original.getRelativeName(), original.getSize(), delby, delDate, original.getVersion() + 1, true, original.getFolderInfo()); } else if (original.isDiretory()) { return new DirectoryInfo(original.getRelativeName(), 0L, delby, delDate, original.getVersion() + 1, true, original.getFolderInfo()); } else { throw new IllegalArgumentException("Illegal original FileInfo: " + original.getClass() + ": " + original.toDetailString()); } } public static FileInfo archivedFile(FolderInfo foInfo, String name, long size, MemberInfo modby, Date modDate, int version) { return new FileInfo(name, size, modby, modDate, version, false, foInfo); } public static DirectoryInfo createBaseDirectoryInfo(FolderInfo foInfo) { return new DirectoryInfo(foInfo, ""); } private static final String[] ILLEGAL_WINDOWS_CHARS = {"|", "?", "\"", "*", "<", ":", ">", "\r"}; /** * #2480: Encodes illegal characters in filenames for windows such as: |, :, * <, >, * * @param relativeFilename * containing the illegal chars, e.g. "My|File.txt" * @return */ public static String encodeIllegalChars(String relativeFilename) { if (!OSUtil.isWindowsSystem()) { return relativeFilename; } String output = relativeFilename; for (String illChar : ILLEGAL_WINDOWS_CHARS) { if (output.contains(illChar)) { String replacement = Base64.encodeString(illChar); replacement = replacement.replace("=", ""); replacement = "$%" + replacement + "%$"; output = output.replace(illChar, replacement); } } if (output.length() > 1) { char lastChar = output.charAt(output.length() - 1); if (lastChar == ' ' || lastChar == '.') { String replacement = Base64.encodeString(String.valueOf(output .charAt(output.length() - 1))); replacement = replacement.replace("=", ""); replacement = "$%" + replacement + "%$"; output = output.substring(0, output.length() - 1); output += replacement; } } if (output.contains(" /")) { String replacement = Base64.encodeString(" "); replacement = replacement.replace("=", ""); replacement = "$%" + replacement + "%$"; output = output.replace(" /", replacement + "/"); } if (output.contains("./")) { String replacement = Base64.encodeString("."); replacement = replacement.replace("=", ""); replacement = "$%" + replacement + "%$"; output = output.replace("./", replacement + "/"); } // Spaces at start and end return output; } /** * #2480: Decodes illegal characters in filenames for windows such as: |, :, * <, >, * * @param relativeFilename * containing the legal chars, e.g. "My$%fA%$File.txt" * @return */ public static String decodeIllegalChars(String relativeFilename) { if (!OSUtil.isWindowsSystem()) { return relativeFilename; } String output = relativeFilename; int start = 0; while ((start = output.indexOf("$%", start)) >= 0) { int end = output.indexOf("%$", start); if (end < 0) { break; } String encoded = output.substring(start + 2, end); String decoded = Base64.decodeString(encoded + "=="); output = output.substring(0, start) + decoded + output.substring(end + 2); } return output; } protected static String buildFileName(File baseDirectory, File file) { if (file.equals(baseDirectory)) { return ""; } String fn = decodeIllegalChars(file.getName()); if (fn.endsWith("/")) { fn = fn.substring(0, fn.length() - 1); } File parent = file.getParentFile(); while (!baseDirectory.equals(parent)) { if (parent == null) { throw new IllegalArgumentException( "Local file seems not to be in a subdir of the local powerfolder copy. Basedir: " + baseDirectory + ", file: " + file); } fn = decodeIllegalChars(parent.getName()) + '/' + fn; parent = parent.getParentFile(); } if (fn.endsWith("/")) { // Crop off last / fn = fn.substring(0, fn.length() - 1); } while (fn.startsWith("/")) { // Crop off first /s fn = fn.substring(1); } return fn; } }