| 1 | package org.farng.mp3.filename; |
| 2 | |
| 3 | import org.farng.mp3.MP3File; |
| 4 | import org.farng.mp3.TagException; |
| 5 | import org.farng.mp3.TagOptionSingleton; |
| 6 | import org.farng.mp3.TagUtility; |
| 7 | import org.farng.mp3.id3.ID3v2_4; |
| 8 | |
| 9 | import java.util.Iterator; |
| 10 | |
| 11 | /** |
| 12 | * This class builds a <code>FilenameTag</code>. The main method to call is <code>createFilenameTagFromMP3File</code>. |
| 13 | * Other methods are public in order to update parts of the tag if needed. |
| 14 | * |
| 15 | * @author Eric Farng |
| 16 | * @version $Revision: 1.2 $ |
| 17 | */ |
| 18 | public class FilenameTagBuilder { |
| 19 | |
| 20 | private FilenameTagBuilder() { |
| 21 | super(); |
| 22 | } |
| 23 | |
| 24 | /** |
| 25 | * Creates a FilenameComposite tree with the given string. It is parsed according to the different values that can |
| 26 | * be set in <code>TagOptionSingleton</code> class |
| 27 | * |
| 28 | * @param token filename to parse |
| 29 | * |
| 30 | * @return FilenameComposite tree representing the given token. |
| 31 | * |
| 32 | * @throws TagException is thrown if there are unmatched parenthesis |
| 33 | */ |
| 34 | public static AbstractFilenameComposite createCompositeFromToken(final String token) throws TagException { |
| 35 | String[] splitToken; |
| 36 | AbstractFilenameComposite composite = null; |
| 37 | final AbstractFilenameComposite beforeComposite; |
| 38 | final AbstractFilenameComposite middleComposite; |
| 39 | final AbstractFilenameComposite afterComposite; |
| 40 | splitToken = parseParenthesis(token); |
| 41 | if (splitToken != null) { |
| 42 | composite = new FilenameParenthesis(); |
| 43 | ((FilenameParenthesis) composite).setOpenDelimiter(splitToken[0]); |
| 44 | beforeComposite = createCompositeFromToken(splitToken[2]); |
| 45 | ((FilenameParenthesis) composite).setBeforeComposite(beforeComposite); |
| 46 | middleComposite = createCompositeFromToken(splitToken[3]); |
| 47 | ((FilenameParenthesis) composite).setMiddleComposite(middleComposite); |
| 48 | afterComposite = createCompositeFromToken(splitToken[4]); |
| 49 | ((FilenameParenthesis) composite).setAfterComposite(afterComposite); |
| 50 | composite.setOriginalToken(token); |
| 51 | return composite; |
| 52 | } |
| 53 | splitToken = parseDelimiter(token); |
| 54 | if (splitToken != null) { |
| 55 | composite = new FilenameDelimiter(); |
| 56 | ((FilenameDelimiter) composite).setDelimiter(splitToken[0]); |
| 57 | beforeComposite = createCompositeFromToken(splitToken[1]); |
| 58 | ((FilenameDelimiter) composite).setBeforeComposite(beforeComposite); |
| 59 | afterComposite = createCompositeFromToken(splitToken[2]); |
| 60 | ((FilenameDelimiter) composite).setAfterComposite(afterComposite); |
| 61 | composite.setOriginalToken(token); |
| 62 | return composite; |
| 63 | } |
| 64 | splitToken = parseStartWordDelimiter(token); |
| 65 | if (splitToken != null) { |
| 66 | composite = new FilenameStartWordDelimiter(); |
| 67 | ((FilenameDelimiter) composite).setDelimiter(splitToken[0]); |
| 68 | beforeComposite = createCompositeFromToken(splitToken[1]); |
| 69 | ((FilenameStartWordDelimiter) composite).setBeforeComposite(beforeComposite); |
| 70 | afterComposite = createCompositeFromToken(splitToken[2]); |
| 71 | ((FilenameStartWordDelimiter) composite).setAfterComposite(afterComposite); |
| 72 | composite.setOriginalToken(token); |
| 73 | return composite; |
| 74 | } |
| 75 | splitToken = parseEndWordDelimiter(token); |
| 76 | if (splitToken != null) { |
| 77 | composite = new FilenameEndWordDelimiter(); |
| 78 | ((FilenameDelimiter) composite).setDelimiter(splitToken[0]); |
| 79 | beforeComposite = createCompositeFromToken(splitToken[1]); |
| 80 | ((FilenameEndWordDelimiter) composite).setBeforeComposite(beforeComposite); |
| 81 | afterComposite = createCompositeFromToken(splitToken[2]); |
| 82 | ((FilenameEndWordDelimiter) composite).setAfterComposite(afterComposite); |
| 83 | composite.setOriginalToken(token); |
| 84 | return composite; |
| 85 | } |
| 86 | if (token != null && token.trim().length() > 0) { |
| 87 | composite = new FilenameToken(); |
| 88 | ((FilenameToken) composite).setToken(token.trim()); |
| 89 | composite.setOriginalToken(token); |
| 90 | return composite; |
| 91 | } |
| 92 | return composite; |
| 93 | } |
| 94 | |
| 95 | public static FilenameTag createEmptyFilenameTag() { |
| 96 | final FilenameTag filenameTag = new FilenameTag(); |
| 97 | filenameTag.setId3tag(new ID3v2_4()); |
| 98 | return filenameTag; |
| 99 | } |
| 100 | |
| 101 | /** |
| 102 | * This method will create a complete FilenameTag from the given mp3File and from the options in |
| 103 | * <code>TagOptionSingleton</code>. This will call all other necessary methods in this builder class. |
| 104 | * |
| 105 | * @param mp3File MP3 file to create the FilenameTag from. |
| 106 | * |
| 107 | * @return FilenameTag of the mp3File argument |
| 108 | * |
| 109 | * @throws Exception is thrown on any IO errors, or parsing errors such as unmatched parenthesis |
| 110 | */ |
| 111 | public static FilenameTag createFilenameTagFromMP3File(final MP3File mp3File) throws Exception { |
| 112 | FilenameTag filenameTag = null; |
| 113 | if (mp3File.getMp3file() != null) { |
| 114 | filenameTag = new FilenameTag(); |
| 115 | final AbstractFilenameComposite composite; |
| 116 | final ID3v2_4 id3tag; |
| 117 | String filename = mp3File.getMp3file().getName(); |
| 118 | final int index = filename.lastIndexOf((int) '.'); |
| 119 | if (index >= 0) { |
| 120 | filenameTag.setExtension(filename.substring(index + 1)); |
| 121 | filename = filename.substring(0, index); |
| 122 | } |
| 123 | |
| 124 | // create composite |
| 125 | composite = createCompositeFromToken(filename); |
| 126 | updateCompositeFromAllTag(composite, mp3File); |
| 127 | updateCompositeFromAllOption(composite); |
| 128 | |
| 129 | // create tag |
| 130 | id3tag = composite.createId3Tag(); |
| 131 | |
| 132 | // assign values; |
| 133 | filenameTag.setMp3file(mp3File); |
| 134 | filenameTag.setComposite(composite); |
| 135 | filenameTag.setId3tag(id3tag); |
| 136 | } |
| 137 | return filenameTag; |
| 138 | } |
| 139 | |
| 140 | /** |
| 141 | * Traverse the composite and set the class field to match keywords found in TagOptionSingleton. |
| 142 | * |
| 143 | * @param composite composite to update. |
| 144 | */ |
| 145 | public static void updateCompositeFromAllOption(final AbstractFilenameComposite composite) { |
| 146 | final Iterator iterator = TagOptionSingleton.getInstance().getKeywordIterator(); |
| 147 | while (iterator.hasNext()) { |
| 148 | composite.matchAgainstKeyword((Class) iterator.next()); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Traverse the composite and set the class field to match frames from all three other tags that are already found |
| 154 | * in the MP3 file. |
| 155 | * |
| 156 | * @param composite composite to update |
| 157 | * @param mp3File mp3file to match all it's tags against. |
| 158 | */ |
| 159 | public static void updateCompositeFromAllTag(final AbstractFilenameComposite composite, final MP3File mp3File) { |
| 160 | composite.matchAgainstTag(mp3File.getID3v1Tag()); |
| 161 | composite.matchAgainstTag(mp3File.getID3v2Tag()); |
| 162 | composite.matchAgainstTag(mp3File.getLyrics3Tag()); |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Parses the given token into two halves with the delimiters found in <code> TagOptionSingleton</code> |
| 167 | * |
| 168 | * @param token token to split |
| 169 | * |
| 170 | * @return index 0 is the delimiter. index 1 and 2 are the before and after tokens respectively. |
| 171 | */ |
| 172 | private static String[] parseDelimiter(final String token) { |
| 173 | String[] tokenArray = null; |
| 174 | if (token != null && token.length() > 0) { |
| 175 | final Iterator iterator = TagOptionSingleton.getInstance().getFilenameDelimiterIterator(); |
| 176 | int index; |
| 177 | String delimiter; |
| 178 | while (iterator.hasNext()) { |
| 179 | delimiter = (String) iterator.next(); |
| 180 | index = token.indexOf(delimiter); |
| 181 | if (index >= 0) { |
| 182 | tokenArray = new String[3]; |
| 183 | tokenArray[0] = delimiter; |
| 184 | tokenArray[1] = token.substring(0, index); |
| 185 | tokenArray[2] = token.substring(index + delimiter.length()); |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | return tokenArray; |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * Parses the given token into two halves with the delimiters found in <code> TagOptionSingleton</code> |
| 194 | * |
| 195 | * @param token token to split |
| 196 | * |
| 197 | * @return index 0 is the delimiter. index 1 and 2 are the before and after tokens respectively. |
| 198 | */ |
| 199 | private static String[] parseEndWordDelimiter(final String token) { |
| 200 | String[] tokenArray = null; |
| 201 | if (token != null && token.length() > 0) { |
| 202 | final Iterator iterator = TagOptionSingleton.getInstance().getEndWordDelimiterIterator(); |
| 203 | int index; |
| 204 | String delimiter; |
| 205 | while (iterator.hasNext()) { |
| 206 | delimiter = (String) iterator.next(); |
| 207 | if (token.endsWith(delimiter)) { |
| 208 | index = token.substring(0, token.length() - delimiter.length()).indexOf(delimiter); |
| 209 | } else { |
| 210 | index = token.indexOf(delimiter); |
| 211 | } |
| 212 | if (index > 0) { |
| 213 | tokenArray = new String[3]; |
| 214 | tokenArray[0] = delimiter; |
| 215 | tokenArray[1] = token.substring(0, index); |
| 216 | tokenArray[2] = token.substring(index); |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | return tokenArray; |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Given a specific token, parse it into halves according to the <code>TagOptionSingleton</code> |
| 225 | * |
| 226 | * @param token token to split. |
| 227 | * |
| 228 | * @return index 0 and 1 are the parenthesis delimiters. index 2, 3, 4 are before, middle, and after respectively. |
| 229 | */ |
| 230 | private static String[] parseParenthesis(final String token) throws TagException { |
| 231 | String[] tokenArray = null; |
| 232 | if (token != null && token.length() > 0) { |
| 233 | final TagOptionSingleton option = TagOptionSingleton.getInstance(); |
| 234 | String tempOpen; |
| 235 | String open = ""; |
| 236 | final String close; |
| 237 | int openIndex = token.length(); |
| 238 | int tempIndex; |
| 239 | final int closeIndex; |
| 240 | final Iterator iterator = option.getOpenParenthesisIterator(); |
| 241 | |
| 242 | // find first parenthesis |
| 243 | while (iterator.hasNext()) { |
| 244 | tempOpen = (String) iterator.next(); |
| 245 | tempIndex = token.indexOf(tempOpen); |
| 246 | if (tempIndex >= 0 && tempIndex < openIndex) { |
| 247 | openIndex = tempIndex; |
| 248 | open = tempOpen; |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | // we have a parenthesis |
| 253 | if (openIndex >= 0 && openIndex < token.length()) { |
| 254 | close = option.getCloseParenthesis(open); |
| 255 | closeIndex = TagUtility.findMatchingParenthesis(token, openIndex); |
| 256 | if (closeIndex < 0) { |
| 257 | throw new TagException("Unmatched parenthesis in \"" + token + "\" at position : " + openIndex); |
| 258 | } |
| 259 | tokenArray = new String[5]; |
| 260 | tokenArray[0] = open; |
| 261 | tokenArray[1] = close; |
| 262 | tokenArray[2] = token.substring(0, openIndex); |
| 263 | tokenArray[3] = token.substring(openIndex + open.length(), closeIndex); |
| 264 | tokenArray[4] = token.substring(closeIndex + close.length()); |
| 265 | } |
| 266 | } |
| 267 | return tokenArray; |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * Parses the given token into two halves with the delimiters found in <code> TagOptionSingleton</code> |
| 272 | * |
| 273 | * @param token token to split |
| 274 | * |
| 275 | * @return index 0 is the delimiter. index 1 and 2 are the before and after tokens respectively. |
| 276 | */ |
| 277 | private static String[] parseStartWordDelimiter(final String token) { |
| 278 | String[] tokenArray = null; |
| 279 | if (token != null && token.length() > 0) { |
| 280 | final Iterator iterator = TagOptionSingleton.getInstance().getStartWordDelimiterIterator(); |
| 281 | int index; |
| 282 | String delimiter; |
| 283 | while (iterator.hasNext()) { |
| 284 | delimiter = (String) iterator.next(); |
| 285 | if (token.startsWith(delimiter)) { |
| 286 | index = token.indexOf(delimiter, delimiter.length()); |
| 287 | } else { |
| 288 | index = token.indexOf(delimiter); |
| 289 | } |
| 290 | if (index > 0) { |
| 291 | tokenArray = new String[3]; |
| 292 | tokenArray[0] = delimiter; |
| 293 | tokenArray[1] = token.substring(0, index); |
| 294 | tokenArray[2] = token.substring(index); |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | return tokenArray; |
| 299 | } |
| 300 | } |