1
package org.jaudiotagger.tag.datatype;
3
import org.jaudiotagger.tag.InvalidDataTypeException;
4
import org.jaudiotagger.tag.TagOptionSingleton;
5
import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
6
import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
8
import java.nio.ByteBuffer;
9
import java.nio.CharBuffer;
10
import java.nio.charset.*;
11
import java.util.regex.Matcher;
12
import java.util.regex.Pattern;
15
* Represents the form 01/10 whereby the second part is optional. This is used by frame such as TRCK and TPOS
17
* Some applications like to prepend the count with a zero to aid sorting, (i.e 02 comes before 10)
19
@SuppressWarnings({"EmptyCatchBlock"})
20
public class PartOfSet extends AbstractString
23
* Creates a new empty PartOfSet datatype.
25
* @param identifier identifies the frame type
28
public PartOfSet(String identifier, AbstractTagFrameBody frameBody)
30
super(identifier, frameBody);
38
public PartOfSet(PartOfSet object)
43
public boolean equals(Object obj)
45
return obj instanceof PartOfSet && super.equals(obj);
49
* Read a 'n' bytes from buffer into a String where n is the framesize - offset
50
* so thefore cannot use this if there are other objects after it because it has no
53
* Must take into account the text encoding defined in the Encoding Object
54
* ID3 Text Frames often allow multiple strings seperated by the null char
55
* appropriate for the encoding.
57
* @param arr this is the buffer for the frame
58
* @param offset this is where to start reading in the buffer for this field
59
* @throws NullPointerException
60
* @throws IndexOutOfBoundsException
62
public void readByteArray(byte[] arr, int offset) throws InvalidDataTypeException
64
logger.finest("Reading from array from offset:" + offset);
66
//Get the Specified Decoder
67
String charSetName = getTextEncodingCharSet();
68
CharsetDecoder decoder = Charset.forName(charSetName).newDecoder();
70
//Decode sliced inBuffer
71
ByteBuffer inBuffer = ByteBuffer.wrap(arr, offset, arr.length - offset).slice();
72
CharBuffer outBuffer = CharBuffer.allocate(arr.length - offset);
74
CoderResult coderResult = decoder.decode(inBuffer, outBuffer, true);
75
if (coderResult.isError())
77
logger.warning("Decoding error:" + coderResult.toString());
79
decoder.flush(outBuffer);
83
String stringValue = outBuffer.toString();
84
value = new PartOfSetValue(stringValue);
86
//SetSize, important this is correct for finding the next datatype
87
setSize(arr.length - offset);
88
logger.info("Read SizeTerminatedString:" + value + " size:" + size);
92
* Write String into byte array
94
* It will remove a trailing null terminator if exists if the option
95
* RemoveTrailingTerminatorOnWrite has been set.
97
* @return the data as a byte array in format to write to file
99
public byte[] writeByteArray()
101
String value = getValue().toString();
103
//Try and write to buffer using the CharSet defined by getTextEncodingCharSet()
106
if (TagOptionSingleton.getInstance().isRemoveTrailingTerminatorOnWrite())
108
if (value.length() > 0)
110
if (value.charAt(value.length() - 1) == '\0')
112
value = value.substring(0, value.length() - 1);
117
String charSetName = getTextEncodingCharSet();
118
if (charSetName.equals(TextEncoding.CHARSET_UTF_16))
120
charSetName = TextEncoding.CHARSET_UTF_16_ENCODING_FORMAT;
121
CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
122
//Note remember LE BOM is ff fe but this is handled by encoder Unicode char is fe ff
123
ByteBuffer bb = encoder.encode(CharBuffer.wrap('\ufeff' + value));
124
data = new byte[bb.limit()];
125
bb.get(data, 0, bb.limit());
130
CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
131
ByteBuffer bb = encoder.encode(CharBuffer.wrap( value));
132
data = new byte[bb.limit()];
133
bb.get(data, 0, bb.limit());
136
//Should never happen so if does throw a RuntimeException
137
catch (CharacterCodingException ce)
139
logger.severe(ce.getMessage());
140
throw new RuntimeException(ce);
142
setSize(data.length);
147
* Get the text encoding being used.
149
* The text encoding is defined by the frame body that the text field belongs to.
151
* @return the text encoding charset
153
protected String getTextEncodingCharSet()
155
byte textEncoding = this.getBody().getTextEncoding();
156
String charSetName = TextEncoding.getInstanceOf().getValueForId(textEncoding);
157
logger.finest("text encoding:" + textEncoding + " charset:" + charSetName);
165
public static class PartOfSetValue
167
private static final Pattern trackNoPatternWithTotalCount;
168
private static final Pattern trackNoPattern;
172
//Match track/total pattern allowing for extraneous nulls ectera at the end
173
trackNoPatternWithTotalCount = Pattern.compile("([0-9]+)/([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
174
trackNoPattern = Pattern.compile("([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
177
private static final String SEPARATOR = "/";
178
private Integer count;
179
private Integer total;
180
private String extra; //Any extraneous info such as null chars
181
public PartOfSetValue()
186
/** When constructing from data
190
public PartOfSetValue(String value)
193
Matcher m = trackNoPatternWithTotalCount.matcher(value);
196
this.count = Integer.parseInt(m.group(1));
197
this.total = Integer.parseInt(m.group(2));
198
this.extra = m.group(3);
202
m = trackNoPattern.matcher(value);
205
this.count = Integer.parseInt(m.group(1));
206
this.extra = m.group(2);
216
public PartOfSetValue(Integer count,Integer total)
224
public Integer getCount()
229
public Integer getTotal()
234
public void setCount(Integer count)
239
public void setTotal(Integer total)
245
public void setCount(String count)
249
this.count=Integer.parseInt(count);
251
catch(NumberFormatException nfe)
257
public void setTotal(String total)
261
this.total=Integer.parseInt(total);
263
catch(NumberFormatException nfe)
269
public String toString()
272
StringBuffer sb = new StringBuffer();
273
if(!TagOptionSingleton.getInstance().isPadNumbers())
277
sb.append(count.intValue());
285
sb.append(SEPARATOR).append(total);
296
if(count>0 && count<10)
298
sb.append("0").append(count);
302
sb.append(count.intValue());
311
if(total>0 && total<10)
313
sb.append(SEPARATOR + "0").append(total);
317
sb.append(SEPARATOR).append(total);
325
return sb.toString();
330
public PartOfSetValue getValue()
332
return (PartOfSetValue)value;
335
public String toString()
337
return value.toString();
1
package org.jaudiotagger.tag.datatype;
3
import org.jaudiotagger.tag.InvalidDataTypeException;
4
import org.jaudiotagger.tag.TagOptionSingleton;
5
import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
6
import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
7
import org.jaudiotagger.utils.EqualsUtil;
9
import java.nio.ByteBuffer;
10
import java.nio.CharBuffer;
11
import java.nio.charset.*;
12
import java.util.regex.Matcher;
13
import java.util.regex.Pattern;
16
* Represents the form 01/10 whereby the second part is optional. This is used by frame such as TRCK and TPOS
18
* Some applications like to prepend the count with a zero to aid sorting, (i.e 02 comes before 10)
20
@SuppressWarnings({"EmptyCatchBlock"})
21
public class PartOfSet extends AbstractString
24
* Creates a new empty PartOfSet datatype.
26
* @param identifier identifies the frame type
29
public PartOfSet(String identifier, AbstractTagFrameBody frameBody)
31
super(identifier, frameBody);
39
public PartOfSet(PartOfSet object)
44
public boolean equals(Object obj)
51
if (!(obj instanceof PartOfSet))
56
PartOfSet that = (PartOfSet) obj;
58
return EqualsUtil.areEqual(value, that.value);
62
* Read a 'n' bytes from buffer into a String where n is the framesize - offset
63
* so thefore cannot use this if there are other objects after it because it has no
66
* Must take into account the text encoding defined in the Encoding Object
67
* ID3 Text Frames often allow multiple strings seperated by the null char
68
* appropriate for the encoding.
70
* @param arr this is the buffer for the frame
71
* @param offset this is where to start reading in the buffer for this field
72
* @throws NullPointerException
73
* @throws IndexOutOfBoundsException
75
public void readByteArray(byte[] arr, int offset) throws InvalidDataTypeException
77
logger.finest("Reading from array from offset:" + offset);
79
//Get the Specified Decoder
80
String charSetName = getTextEncodingCharSet();
81
CharsetDecoder decoder = Charset.forName(charSetName).newDecoder();
83
//Decode sliced inBuffer
84
ByteBuffer inBuffer = ByteBuffer.wrap(arr, offset, arr.length - offset).slice();
85
CharBuffer outBuffer = CharBuffer.allocate(arr.length - offset);
87
CoderResult coderResult = decoder.decode(inBuffer, outBuffer, true);
88
if (coderResult.isError())
90
logger.warning("Decoding error:" + coderResult.toString());
92
decoder.flush(outBuffer);
96
String stringValue = outBuffer.toString();
97
value = new PartOfSetValue(stringValue);
99
//SetSize, important this is correct for finding the next datatype
100
setSize(arr.length - offset);
101
logger.info("Read SizeTerminatedString:" + value + " size:" + size);
105
* Write String into byte array
107
* It will remove a trailing null terminator if exists if the option
108
* RemoveTrailingTerminatorOnWrite has been set.
110
* @return the data as a byte array in format to write to file
112
public byte[] writeByteArray()
114
String value = getValue().toString();
116
//Try and write to buffer using the CharSet defined by getTextEncodingCharSet()
119
if (TagOptionSingleton.getInstance().isRemoveTrailingTerminatorOnWrite())
121
if (value.length() > 0)
123
if (value.charAt(value.length() - 1) == '\0')
125
value = value.substring(0, value.length() - 1);
130
String charSetName = getTextEncodingCharSet();
131
if (charSetName.equals(TextEncoding.CHARSET_UTF_16))
133
charSetName = TextEncoding.CHARSET_UTF_16_ENCODING_FORMAT;
134
CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
135
//Note remember LE BOM is ff fe but this is handled by encoder Unicode char is fe ff
136
ByteBuffer bb = encoder.encode(CharBuffer.wrap('\ufeff' + value));
137
data = new byte[bb.limit()];
138
bb.get(data, 0, bb.limit());
143
CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
144
ByteBuffer bb = encoder.encode(CharBuffer.wrap( value));
145
data = new byte[bb.limit()];
146
bb.get(data, 0, bb.limit());
149
//Should never happen so if does throw a RuntimeException
150
catch (CharacterCodingException ce)
152
logger.severe(ce.getMessage());
153
throw new RuntimeException(ce);
155
setSize(data.length);
160
* Get the text encoding being used.
162
* The text encoding is defined by the frame body that the text field belongs to.
164
* @return the text encoding charset
166
protected String getTextEncodingCharSet()
168
byte textEncoding = this.getBody().getTextEncoding();
169
String charSetName = TextEncoding.getInstanceOf().getValueForId(textEncoding);
170
logger.finest("text encoding:" + textEncoding + " charset:" + charSetName);
178
public static class PartOfSetValue
180
private static final Pattern trackNoPatternWithTotalCount;
181
private static final Pattern trackNoPattern;
185
//Match track/total pattern allowing for extraneous nulls ectera at the end
186
trackNoPatternWithTotalCount = Pattern.compile("([0-9]+)/([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
187
trackNoPattern = Pattern.compile("([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
190
private static final String SEPARATOR = "/";
191
private Integer count;
192
private Integer total;
193
private String extra; //Any extraneous info such as null chars
194
public PartOfSetValue()
199
/** When constructing from data
203
public PartOfSetValue(String value)
206
Matcher m = trackNoPatternWithTotalCount.matcher(value);
209
this.count = Integer.parseInt(m.group(1));
210
this.total = Integer.parseInt(m.group(2));
211
this.extra = m.group(3);
215
m = trackNoPattern.matcher(value);
218
this.count = Integer.parseInt(m.group(1));
219
this.extra = m.group(2);
229
public PartOfSetValue(Integer count,Integer total)
237
public Integer getCount()
242
public Integer getTotal()
247
public void setCount(Integer count)
252
public void setTotal(Integer total)
258
public void setCount(String count)
262
this.count=Integer.parseInt(count);
264
catch(NumberFormatException nfe)
270
public void setTotal(String total)
274
this.total=Integer.parseInt(total);
276
catch(NumberFormatException nfe)
282
public String toString()
285
StringBuffer sb = new StringBuffer();
286
if(!TagOptionSingleton.getInstance().isPadNumbers())
290
sb.append(count.intValue());
298
sb.append(SEPARATOR).append(total);
309
if(count>0 && count<10)
311
sb.append("0").append(count);
315
sb.append(count.intValue());
324
if(total>0 && total<10)
326
sb.append(SEPARATOR + "0").append(total);
330
sb.append(SEPARATOR).append(total);
338
return sb.toString();
342
public boolean equals(Object obj)
349
if (!(obj instanceof PartOfSetValue))
354
PartOfSetValue that = (PartOfSetValue) obj;
357
EqualsUtil.areEqual(getCount(), that.getCount()) &&
358
EqualsUtil.areEqual(getTotal(), that.getTotal());
364
public PartOfSetValue getValue()
366
return (PartOfSetValue)value;
369
public String toString()
371
return value.toString();
b'\\ No newline at end of file'