/* * Copyright 2004 - 2008 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$ */ package de.dal33t.powerfolder.light; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.Entity; import javax.persistence.Id; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Index; import de.dal33t.powerfolder.Constants; import de.dal33t.powerfolder.Controller; import de.dal33t.powerfolder.disk.Folder; import de.dal33t.powerfolder.util.Util; import de.dal33t.powerfolder.util.intern.FolderInfoInternalizer; import de.dal33t.powerfolder.util.intern.Internalizer; /** * A Folder hash info * * @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc </a> * @version $Revision: 1.9 $ */ @Entity @Immutable @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class FolderInfo implements Serializable, Cloneable { private static final long serialVersionUID = 102L; private static final Internalizer<FolderInfo> INTERNALIZER = new FolderInfoInternalizer(); public static final String PROPERTYNAME_ID = "id"; public static final String PROPERTYNAME_NAME = "name"; @Index(name="IDX_FOLDER_NAME") public String name; @Id public String id; /** * The cached hash info. */ private transient int hash; private FolderInfo() { // NOP - for Hibernate } public FolderInfo(Folder folder) { name = folder.getName(); id = folder.getId(); hash = hashCode0(); } public FolderInfo(String name, String id) { this.name = name; this.id = id; hash = hashCode0(); } public boolean isMetaFolder() { // #1548: Convert this into boolean flag? return id != null && id.startsWith(Constants.METAFOLDER_ID_PREFIX); } /** * @return the {@link FolderInfo} of the PARENT folder if this is a meta * folder. */ public FolderInfo getParentFolderInfo() { if (!isMetaFolder()) { Logger.getLogger(FolderInfo.class.getName()).log( Level.WARNING, "Not required to retrieve parent folder info on non-meta folder: " + this, new RuntimeException("from here")); return this; } try { int i = id.indexOf(Constants.METAFOLDER_ID_PREFIX); String folderId = id.substring(i + Constants.METAFOLDER_ID_PREFIX.length()); i = name.indexOf(Constants.METAFOLDER_ID_PREFIX); String folderName = name.substring(i + Constants.METAFOLDER_ID_PREFIX.length()); return new FolderInfo(folderName, folderId).intern(); } catch (Exception e) { Logger.getLogger(FolderInfo.class.getName()).log(Level.WARNING, "Unable to get parent folder info for meta-folder: " + this, e); return this; } } /** * @return the meta-folder info for this folder */ public FolderInfo getMetaFolderInfo() { if (isMetaFolder()) { Logger.getLogger(FolderInfo.class.getName()).log(Level.WARNING, "Tried to retrieve meta-folder for meta-folder: " + this, new RuntimeException("from here")); return this; } return new FolderInfo(Constants.METAFOLDER_ID_PREFIX + name, Constants.METAFOLDER_ID_PREFIX + id); } public String getId() { return id; } public String getName() { return name; } /** * Returns the joined folder, or null if folder is not joined * * @param controller * @return the folder */ public Folder getFolder(Controller controller) { return controller.getFolderRepository().getFolder(this); } // Security **************************************************************** /** * Calculates the secure Id for this folder with magicid from remote * * @param magicId * @return the secure Id for this folder with magicid from remote */ public String calculateSecureId(String magicId) { // Do the magic... try { byte[] mId = magicId.getBytes("UTF-8"); byte[] fId = id.getBytes("UTF-8"); byte[] hexId = new byte[mId.length * 2 + fId.length]; // Build secure ID base: [MAGIC_ID][FOLDER_ID][MAGIC_ID] System.arraycopy(mId, 0, hexId, 0, mId.length); System.arraycopy(fId, 0, hexId, mId.length - 1, fId.length); System.arraycopy(mId, 0, hexId, mId.length + fId.length - 2, mId.length); return new String(Util.encodeHex(Util.md5(hexId))); } catch (UnsupportedEncodingException e) { throw (IllegalStateException) new IllegalStateException( "Fatal problem: UTF-8 encoding not found").initCause(e); } } /* * General */ @Override public int hashCode() { if (hash == 0) { // Oh! Default value. Better recalculate hashcode cache hash = hashCode0(); } return hash; } private int hashCode0() { return (id == null) ? 0 : id.hashCode(); } public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof FolderInfo) { FolderInfo otherInfo = (FolderInfo) other; return Util.equals(this.id, otherInfo.id); } return false; } public FolderInfo intern() { return INTERNALIZER.intern(this); } // used for sorting ignores case public int compareTo(Object other) { FolderInfo otherFolderInfo = (FolderInfo) other; return name.compareToIgnoreCase(otherFolderInfo.name); } public String toString() { return "Folder '" + name + '\''; } // Serialization optimization ********************************************* private static final long extVersionUID = 100L; public static FolderInfo readExt(ObjectInput in) throws IOException, ClassNotFoundException { FolderInfo folderInfo = new FolderInfo(); folderInfo.readExternal(in); return folderInfo; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { long extUID = in.readLong(); if (extUID != extVersionUID) { throw new InvalidClassException(this.getClass().getName(), "Unable to read. extVersionUID(steam): " + extUID + ", expected: " + extVersionUID); } id = in.readUTF(); name = in.readUTF(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(extVersionUID); out.writeUTF(id); out.writeUTF(name); } }