| 1 | package org.farng.mp3.filename; |
| 2 | |
| 3 | import org.farng.mp3.AbstractMP3Tag; |
| 4 | import org.farng.mp3.TagOptionSingleton; |
| 5 | import org.farng.mp3.id3.AbstractFrameBodyTextInformation; |
| 6 | import org.farng.mp3.id3.AbstractFrameBodyUrlLink; |
| 7 | import org.farng.mp3.id3.AbstractID3v2Frame; |
| 8 | import org.farng.mp3.id3.AbstractID3v2FrameBody; |
| 9 | import org.farng.mp3.id3.FrameBodyCOMM; |
| 10 | import org.farng.mp3.id3.ID3v2_4; |
| 11 | import org.farng.mp3.id3.ID3v2_4Frame; |
| 12 | |
| 13 | import java.util.Iterator; |
| 14 | |
| 15 | /** |
| 16 | * This composite subclass is the leaf of the tree. It contains the actual strings found in the filename. |
| 17 | * |
| 18 | * @author Eric Farng |
| 19 | * @version $Revision: 1.7 $ |
| 20 | */ |
| 21 | public class FilenameToken extends AbstractFilenameComposite { |
| 22 | |
| 23 | /** |
| 24 | * what information this object represents. |
| 25 | */ |
| 26 | private Class id3v2FrameBodyClass = null; |
| 27 | /** |
| 28 | * token that this object represents |
| 29 | */ |
| 30 | private String token = null; |
| 31 | |
| 32 | /** |
| 33 | * Creates a new FilenameToken object. |
| 34 | */ |
| 35 | public FilenameToken() { |
| 36 | super(); |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * Creates a new FilenameToken object. |
| 41 | */ |
| 42 | public FilenameToken(final FilenameToken copyObject) { |
| 43 | super(copyObject); |
| 44 | try { |
| 45 | id3v2FrameBodyClass = id3v2FrameBodyClass.newInstance().getClass(); |
| 46 | } catch (IllegalAccessException ex) { |
| 47 | throw new NullPointerException("IllegalAccessException: No access to run constructor to create copy " + |
| 48 | ex.getMessage()); |
| 49 | } catch (InstantiationException ex) { |
| 50 | throw new NullPointerException("InstantiationException: Unable to instantiate constructor to copy " + |
| 51 | ex.getMessage()); |
| 52 | } |
| 53 | token = copyObject.token; |
| 54 | } |
| 55 | |
| 56 | public void setFrame(final AbstractID3v2Frame frame) { |
| 57 | if (id3v2FrameBodyClass != null && id3v2FrameBodyClass.equals(frame.getBody().getClass())) { |
| 58 | //todo add support for more tag matches. only doing text |
| 59 | // information and URL links right now because i'm lazy |
| 60 | if (AbstractFrameBodyTextInformation.class.isInstance(frame.getBody())) { |
| 61 | token = ((AbstractFrameBodyTextInformation) frame.getBody()).getText(); |
| 62 | } else if (AbstractFrameBodyUrlLink.class.isInstance(frame.getBody())) { |
| 63 | token = ((AbstractFrameBodyUrlLink) frame.getBody()).getUrlLink(); |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * Sets the ID3v2 frame body that this token represents |
| 70 | * |
| 71 | * @param id3v2FrameBodyClass the ID3v2 frame body that this token represents |
| 72 | */ |
| 73 | public void setId3v2FrameBodyClass(final Class id3v2FrameBodyClass) { |
| 74 | this.id3v2FrameBodyClass = id3v2FrameBodyClass; |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Returns the ID3v2 frame body that this token represents |
| 79 | * |
| 80 | * @return the ID3v2 frame body that this token represents |
| 81 | */ |
| 82 | public Class getId3v2FrameBodyClass() { |
| 83 | return id3v2FrameBodyClass; |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Sets the token that this class contains |
| 88 | * |
| 89 | * @param token the token that this class contains |
| 90 | */ |
| 91 | public void setToken(final String token) { |
| 92 | this.token = token.trim(); |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Return the token that this class contains |
| 97 | * |
| 98 | * @return the token that this class contains |
| 99 | */ |
| 100 | public String getToken() { |
| 101 | return token; |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Reconstruct the filename that is represented by this composite. |
| 106 | * |
| 107 | * @return the filename that is represented by this composite. |
| 108 | */ |
| 109 | public String composeFilename() { |
| 110 | return token; |
| 111 | } |
| 112 | |
| 113 | public ID3v2_4 createId3Tag() { |
| 114 | final ID3v2_4 newTag = new ID3v2_4(); |
| 115 | if (id3v2FrameBodyClass != null) { |
| 116 | try { |
| 117 | final AbstractID3v2FrameBody body = (AbstractID3v2FrameBody) id3v2FrameBodyClass.newInstance(); |
| 118 | |
| 119 | //todo need to add support for more frame bodies here |
| 120 | if (body instanceof AbstractFrameBodyTextInformation) { |
| 121 | ((AbstractFrameBodyTextInformation) body).setText(token); |
| 122 | ((AbstractFrameBodyTextInformation) body).setTextEncoding((byte) 0); |
| 123 | } else if (body instanceof AbstractFrameBodyUrlLink) { |
| 124 | ((AbstractFrameBodyUrlLink) body).setUrlLink(token); |
| 125 | } else if (body instanceof FrameBodyCOMM) { |
| 126 | ((FrameBodyCOMM) body).setText(token); |
| 127 | ((FrameBodyCOMM) body).setDescription(""); |
| 128 | ((FrameBodyCOMM) body).setLanguage(TagOptionSingleton.getInstance().getLanguage()); |
| 129 | ((FrameBodyCOMM) body).setTextEncoding(TagOptionSingleton.getInstance().getTextEncoding()); |
| 130 | } |
| 131 | final ID3v2_4Frame frame = new ID3v2_4Frame(); |
| 132 | frame.setBody(body); |
| 133 | newTag.setFrame(frame); |
| 134 | } catch (IllegalAccessException ex) { |
| 135 | // catch and dont' create the frame |
| 136 | } catch (InstantiationException ex) { |
| 137 | // catch and dont' create the frame |
| 138 | } |
| 139 | } |
| 140 | return newTag; |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Returns an iterator through each <code>FilenameToken</code> in this composite in the correct order for the file |
| 145 | * name. |
| 146 | * |
| 147 | * @return an iterator through each <code>FilenameToken</code> in this composite |
| 148 | */ |
| 149 | public Iterator iterator() { |
| 150 | return new FilenameTokenIterator(this); |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Match all elements of this composite against the keywords for this class type found in |
| 155 | * <code>TagOptionSingleton</code>. If the <code>FilenameToken</code> matches the keyword, the token's class is |
| 156 | * set. |
| 157 | * |
| 158 | * @param matchId3v2FrameBodyClass Class of keywords to match against. |
| 159 | */ |
| 160 | public void matchAgainstKeyword(final Class matchId3v2FrameBodyClass) { |
| 161 | if (AbstractID3v2FrameBody.class.isAssignableFrom(matchId3v2FrameBodyClass)) { |
| 162 | if (TagOptionSingleton.getInstance().isCompositeMatchOverwrite() || id3v2FrameBodyClass == null) { |
| 163 | final Iterator iterator = TagOptionSingleton.getInstance() |
| 164 | .getKeywordListIterator(matchId3v2FrameBodyClass); |
| 165 | final String lowerCaseToken = token.toLowerCase(); |
| 166 | while (iterator.hasNext()) { |
| 167 | final String matchString = ((String) iterator.next()).toLowerCase(); |
| 168 | if (matchString.equals(lowerCaseToken) || |
| 169 | matchString.indexOf(lowerCaseToken) >= 0 || |
| 170 | lowerCaseToken.indexOf(matchString) >= 0) { |
| 171 | setId3v2FrameBodyClass(matchId3v2FrameBodyClass); |
| 172 | break; |
| 173 | } |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * Match all elements of this composite against the given tag. If any element of <code>matchTag</code> matches any |
| 181 | * element of this tag's composite, then this tag's composite leaf node's class is set. |
| 182 | * |
| 183 | * @param matchTag Tag to match against |
| 184 | */ |
| 185 | public void matchAgainstTag(final AbstractMP3Tag matchTag) { |
| 186 | if (TagOptionSingleton.getInstance().isCompositeMatchOverwrite() || id3v2FrameBodyClass == null) { |
| 187 | final ID3v2_4 tag; |
| 188 | if (matchTag instanceof ID3v2_4) { |
| 189 | tag = (ID3v2_4) matchTag; |
| 190 | } else { |
| 191 | tag = new ID3v2_4(matchTag); |
| 192 | } |
| 193 | final Iterator iterator = tag.iterator(); |
| 194 | AbstractID3v2Frame frame; |
| 195 | AbstractID3v2FrameBody body; |
| 196 | String matchString = null; |
| 197 | final String lowerCaseToken = token.toLowerCase(); |
| 198 | while (iterator.hasNext()) { |
| 199 | frame = (ID3v2_4Frame) iterator.next(); |
| 200 | body = (AbstractID3v2FrameBody) frame.getBody(); |
| 201 | //todo add support for more tag matches. only doing text |
| 202 | // information and URL links right now because i'm lazy |
| 203 | if (body instanceof AbstractFrameBodyTextInformation) { |
| 204 | matchString = ((AbstractFrameBodyTextInformation) body).getText(); |
| 205 | if (matchString != null) { |
| 206 | matchString = matchString.toLowerCase(); |
| 207 | } |
| 208 | } else if (body instanceof AbstractFrameBodyUrlLink) { |
| 209 | matchString = ((AbstractFrameBodyUrlLink) body).getUrlLink(); |
| 210 | if (matchString != null) { |
| 211 | matchString = matchString.toLowerCase(); |
| 212 | } |
| 213 | } else if (body instanceof FrameBodyCOMM) { |
| 214 | matchString = ((FrameBodyCOMM) body).getText(); |
| 215 | } |
| 216 | if (lowerCaseToken.equals(matchString) || |
| 217 | matchString != null && |
| 218 | (matchString.indexOf(lowerCaseToken) >= 0 || lowerCaseToken.indexOf(matchString) >= 0)) { |
| 219 | setId3v2FrameBodyClass(body.getClass()); |
| 220 | break; |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Returns a string containing debug information about this class |
| 228 | * |
| 229 | * @return a string containing debug information about this class |
| 230 | */ |
| 231 | public String toString() { |
| 232 | return id3v2FrameBodyClass + ": " + token; |
| 233 | } |
| 234 | } |