001 /** 002 * iFish - An iRiver iHP jukebox database creation tool 003 * 004 * Copyright (C) 2009 Richard "Shred" Körber 005 * http://ifish.shredzone.org 006 * 007 * This program is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * This program is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 019 */ 020 package net.shredzone.ifish.db; 021 022 import java.io.*; 023 024 import net.shredzone.ifish.Util; 025 026 /** 027 * This SmartRenameCallback will try to shorten the filename by applying 028 * a sequence of tricks until the name is short enough. 029 * <ol> 030 * <li>Remove all text in brackets.</li> 031 * <li>Remove all "feat."</li> 032 * <li>From right to left, remove all parts following a minus '-'</li> 033 * <li>From right to left, remove word by word</li> 034 * <li>Just take the first sequence of numbers</li> 035 * <li>Brute force cut after 40 chars.</li> 036 * </ol> 037 * 038 * @author Richard Körber <dev@shredzone.de> 039 * @version $Id: SmartRenameCallback.java 291 2009-04-28 21:29:27Z shred $ 040 */ 041 public class SmartRenameCallback extends RenameCallback.DefaultRenameCallback { 042 043 /** Maximum number to append to make a filename unique. */ 044 private static final int MAX_RENAMECOUNT = 9999; 045 // 9999 may be quite huge, but it's a good value because the iRiver 046 // cannot handle more than 9999 files anyways. 047 048 /** 049 * A file name exceeded the maximum length of 52 characters. 050 * This method will try to truncate the file name, rename the file 051 * and return the new file name. 052 * 053 * @param base Base directory (jukebox mount point) 054 * @param file File with the bad name 055 * @return New file name, null if this file is to be ignored 056 * @throws DatabaseException Directory could not be renamed 057 * @throws IOException An IO error occured during nenaming 058 */ 059 @Override 060 public File renameFile( File base, File file ) 061 throws DatabaseException, IOException { 062 String name = file.getName(); 063 String suffix = ""; 064 int maxlen = 50; 065 int minlen = 4; 066 067 //--- Split suffix --- 068 int spos = name.lastIndexOf( "." ); 069 if( spos>0 ) { 070 suffix = name.substring( spos+1 ); 071 name = name.substring( 0, spos ).trim(); 072 if( suffix.length() > 50 ) { 073 // We won't destroy bogus suffixes. Instead we will bail out 074 // here and let the user correct it himself. 075 return super.renameFile( base, file ); 076 } 077 maxlen -= suffix.length()-1; 078 } 079 080 //--- Remove everything within brackets --- 081 // Usually this already helps a big deal 082 name = name.replaceAll( "[\\(|\\[|\\<].*?[\\)|\\]|\\>]", "" ); 083 084 if( name.length() > maxlen ) { 085 //--- Remove "feat." --- 086 // Removes a "feat." part unless it's at the beginning 087 int fpos = name.toLowerCase().indexOf( "feat." ); 088 if( fpos>minlen ) { 089 name = name.substring( 0, fpos ).trim(); 090 } 091 092 if( name.length() > maxlen ) { 093 //--- Remove all after '-' --- 094 // The name still is too long. Remove everything past a '-' 095 // until the string fits. 096 int mpos = name.lastIndexOf( "-" ); 097 while( name.length()>maxlen && mpos>minlen ) { 098 name = name.substring( 0, mpos ).trim(); 099 mpos = name.lastIndexOf( "-" ); 100 } 101 102 if( name.length() > maxlen ) { 103 //--- Remove word by word --- 104 // Getting desperate... We remove each word from the end of 105 // the name until the string fits. Underscores are regarded 106 // equal to spaces here. 107 int wpos = Math.max( name.lastIndexOf( " " ), name.lastIndexOf( "_" ) ); 108 while( name.length()>maxlen && wpos>minlen ) { 109 name = name.substring( 0, wpos ).trim(); 110 wpos = Math.max( name.lastIndexOf( " " ), name.lastIndexOf( "_" ) ); 111 } 112 113 if( name.length() > maxlen ) { 114 //--- Only take the first numbers --- 115 // At least we will try to keep a track number. We will 116 // only take the first ciphers and minus signs. 117 for( int pos=0; pos<maxlen; pos++ ) { 118 char ch = name.charAt( pos ); 119 if(!( ch=='-' || ( ch>='0' && ch<='9' ) )) { 120 if( pos>0 ) { 121 name = name.substring( 0, pos ); 122 } 123 break; 124 } 125 } 126 127 if( name.length() > maxlen ) { 128 //--- Just cut it off --- 129 // Still too long? I have no more ideas... Just cut off 130 // the name after the maximum number of chars. 131 name = name.substring( 0, maxlen ); 132 133 } 134 } 135 } 136 } 137 } 138 139 name = name.trim(); 140 141 //--- Rename the file --- 142 // We have found a good file name. Now we try to rename the file. 143 // If there is another file with that name, count up a number at 144 // the end of the file until we have found a unique name. 145 if( name.length() > 0 ) { 146 File newname; 147 148 for( int renameCount=0; renameCount<MAX_RENAMECOUNT; renameCount++ ) { 149 String proposal = uniqueName(name, renameCount, maxlen); 150 151 newname = new File( 152 file.getParentFile(), 153 appendSuffix(proposal, suffix) 154 ); 155 156 if(! newname.exists() ) { 157 if(! Util.renameFile( file, newname ) ) { 158 return super.renameFile( base, file ); 159 } 160 return newname; 161 } 162 } 163 } 164 165 //--- Ask the user... --- 166 // We were completely unable to find a proper file name. There is no 167 // chance but to give up ask the user what to do. 168 return super.renameFile( base, file ); 169 } 170 171 /** 172 * Combine given name and suffix to form a filename. 173 * If there is no suffix, return just the name. 174 * 175 * @param name filename w/o suffix 176 * @param suffix file suffix, or empty string 177 * @return filename.suffix, or filename, if suffix is empty 178 */ 179 private String appendSuffix( String name, String suffix ) { 180 return( (suffix.length()>0) ? name+"."+suffix : name); 181 } 182 183 /** 184 * Make string unique by appending a number. 185 * If <code>number</code> is greater than zero, the (decimal 186 * representation of) <code>number</code> is appended to 187 * <code>name</code>. If necessary, <code>name</code> is truncated 188 * beforehand such that the result's length does not exceed 189 * <code>maxlength</code>. 190 * <p> 191 * For <code>number <= 0</code>, just returns <code>name</code>. 192 * 193 * @param name String to modify 194 * @param number number to insert into <code>name</code> 195 * @param maxlength Maximum length of return value. Note: If this is too 196 * short to hold <code>number</code> as a String, this method will throw 197 * an IndexOutOfBoundsException. 198 * @return (possibly) modified <code>name</code> 199 */ 200 private String uniqueName(String name, int number, int maxlength){ 201 if( number < 1 ) 202 return name; 203 204 String decNumber = String.valueOf(number); 205 if( name.length()+decNumber.length() > maxlength ) { 206 // appending would make the name to long, truncate it 207 // If maxlength is too small, we get an IndexOutOfBoundsException here 208 name = name.substring(0, maxlength-decNumber.length()); 209 } 210 name += decNumber; 211 return name; 212 } 213 214 }