1 /* Copyright (C) 2005-2011 Fabio Riccardi */
2 
3 package com.lightcrafts.image.metadata.values;
4 
5 import java.io.IOException;
6 import java.io.ObjectInput;
7 import java.io.ObjectOutput;
8 
9 import com.lightcrafts.image.metadata.ImageMetaType;
10 import com.lightcrafts.utils.LCArrays;
11 import com.lightcrafts.utils.Rational;
12 
13 import static com.lightcrafts.image.metadata.ImageMetaType.META_SRATIONAL;
14 
15 /**
16  * A <code>RationalMetaValue</code> is-a {@link NumericMetaValue} for holding a
17  * {@link Rational} metadata value.
18  *
19  * @author Paul J. Lucas [paul@lightcrafts.com]
20  */
21 public class RationalMetaValue extends NumericMetaValue {
22 
23     ////////// public /////////////////////////////////////////////////////////
24 
25     /**
26      * Construct a <code>RationalMetaValue</code>.
27      * This constructor exists only for externalization.
28      */
RationalMetaValue()29     public RationalMetaValue() {
30         // do nothing
31     }
32 
33     /**
34      * Construct a <code>RationalMetaValue</code>.
35      *
36      * @param numerator The numerator.
37      * @param denominator The denominator.  It must not be zero.
38      */
RationalMetaValue( int numerator, int denominator )39     public RationalMetaValue( int numerator, int denominator ) {
40         this( new Rational( numerator, denominator ) );
41     }
42 
43     /**
44      * Construct a <code>RationalMetaValue</code>.
45      *
46      * @param values The array of values.
47      */
RationalMetaValue( Rational... values )48     public RationalMetaValue( Rational... values ) {
49         m_value = values;
50     }
51 
52     /**
53      * Construct a <code>RationalMetaValue</code>.
54      *
55      * @param values The array of values.
56      */
RationalMetaValue( String... values )57     public RationalMetaValue( String... values ) {
58         setValuesImpl( values );
59     }
60 
61     /**
62      * {@inheritDoc}
63      */
clone()64     public RationalMetaValue clone() {
65         final RationalMetaValue copy = (RationalMetaValue)super.clone();
66         copy.m_value = m_value.clone();
67         return copy;
68     }
69 
70     /**
71      * {@inheritDoc}
72      */
compareTo( Object o )73     public final int compareTo( Object o ) {
74         if ( o instanceof NumericMetaValue ) {
75             final NumericMetaValue rightVal = (NumericMetaValue)o;
76             final double leftDouble = getDoubleValue();
77             final double rightDouble = rightVal.getDoubleValue();
78             return Double.compare( leftDouble, rightDouble );
79         }
80         return super.compareTo( o );
81     }
82 
83     /**
84      * Compares this <code>ImageMetaValue</code> to a {@link String}.  The
85      * string is first parsed as a {@link Rational}, then a numeric comparison
86      * is done.
87      *
88      * @param s The {@link String} to compare to.
89      * @return Returns a negative integer, zero, or a positive integer as this
90      * <code>ImageMetaValue</code> is less than, equal to, or greater than the
91      * {@link Rational} value of the string.  If the string can not be parsed
92      * as a {@link Rational}, returns 1.
93      * @see #compareTo(Object)
94      */
compareTo( String s )95     public final int compareTo( String s ) {
96         try {
97             final Rational rightRational = Rational.parseRational( s );
98             final Rational leftRational = getRationalValue();
99             return leftRational.compareTo( rightRational );
100         }
101         catch ( NumberFormatException e ) {
102             return 1;
103         }
104     }
105 
106     /**
107      * {@inheritDoc}
108      */
getDoubleValue()109     public final double getDoubleValue() {
110         return getRationalValue().doubleValue();
111     }
112 
113     /**
114      * {@inheritDoc}
115      */
getLongValueAt(int index)116     public final long getLongValueAt(int index) {
117         return m_value[index].longValue();
118     }
119 
120     /**
121      * Get the first {@link Rational} value.
122      *
123      * @return Returns said value.
124      */
getRationalValue()125     public final Rational getRationalValue() {
126         return m_value[0];
127     }
128 
129     /**
130      * Get the native {@link Rational} array value.
131      *
132      * @return Returns said array.
133      */
getRationalValues()134     public final Rational[] getRationalValues() {
135         return m_value;
136     }
137 
138     /**
139      * Gets the type of this metadata value.
140      *
141      * @return Always returns <code>META_RATIONAL</code>.
142      */
getType()143     public ImageMetaType getType() {
144         return META_SRATIONAL;
145     }
146 
147     /**
148      * {@inheritDoc}
149      */
getValueCount()150     public final int getValueCount() {
151         return m_value != null ? m_value.length : 0;
152     }
153 
154     /**
155      * {@inheritDoc}
156      */
isLegalValue( String value )157     public boolean isLegalValue( String value ) {
158         try {
159             Rational.parseRational( value );
160             return true;
161         }
162         catch ( IllegalArgumentException e ) {
163             return false;
164         }
165     }
166 
167     /**
168      * {@inheritDoc}
169      */
setLongValue( long newValue )170     public void setLongValue( long newValue ) {
171         setRationalValueAt( new Rational( (int)newValue, 1 ), 0 );
172     }
173 
174     /**
175      * Sets the {@link Rational} value at the given index.
176      *
177      * @param newValue The new value.
178      * @param index The index to set the value of.
179      */
setRationalValueAt( Rational newValue, int index )180     public final synchronized void setRationalValueAt( Rational newValue,
181                                                        int index ) {
182         checkIsEditable();
183         if ( m_value == null )
184             m_value = new Rational[ index + 1 ];
185         else if ( index >= m_value.length )
186             m_value = (Rational[])LCArrays.resize( m_value, index + 1 );
187         m_value[ index ] = newValue;
188         dirty();
189     }
190 
191     /**
192      * {@inheritDoc}
193      */
readExternal( ObjectInput in )194     public final void readExternal( ObjectInput in ) throws IOException {
195         final int length = readHeader( in );
196         m_value = new Rational[ length ];
197         for ( int i = 0; i < length; ++i )
198             m_value[i] = new Rational( in.readInt(), in.readInt() );
199     }
200 
201     /**
202      * @serialData The header (see {@link #writeHeader(ObjectOutput)}) followed
203      * by the {@link Rational} values where each is a pair of <code>int</code>
204      * comprising the numerator and denominator.
205      */
writeExternal( ObjectOutput out )206     public final void writeExternal( ObjectOutput out ) throws IOException {
207         writeHeader( out );
208         for ( Rational value : m_value ) {
209             out.writeInt( value.numerator() );
210             out.writeInt( value.denominator() );
211         }
212     }
213 
214     ////////// protected //////////////////////////////////////////////////////
215 
216     /**
217      * Parse and append a new value.
218      *
219      * @param newValue The new value.
220      */
appendValueImpl( String newValue )221     protected final void appendValueImpl( String newValue ) {
222         final Rational newRational = Rational.parseRational( newValue );
223         if ( m_value == null )
224             m_value = new Rational[] { newRational };
225         else {
226             m_value = (Rational[])LCArrays.resize( m_value, m_value.length + 1 );
227             m_value[ m_value.length - 1 ] = newRational;
228         }
229     }
230 
231     /**
232      * Gets the values as an array of {@link String}.
233      *
234      * @return Returns said array.
235      */
getValuesImpl()236     protected final String[] getValuesImpl() {
237         if ( m_value == null )
238             return null;
239         final String[] value = new String[ m_value.length ];
240         for ( int i = 0; i < m_value.length; ++i )
241             value[i] = m_value[i].toString();
242         return value;
243     }
244 
245     /**
246      * Parse and set the values.
247      *
248      * @param newValue The array of new values.
249      * @throws IllegalArgumentException if any one of the {@link String}s do
250      * not contain a parseable {@link Rational}.
251      */
setValuesImpl( String[] newValue )252     protected final void setValuesImpl( String[] newValue ) {
253         if ( m_value == null || m_value.length != newValue.length )
254             m_value = new Rational[ newValue.length ];
255         for ( int i = 0; i < newValue.length; ++i )
256             m_value[i] = Rational.parseRational( newValue[i] );
257     }
258 
259     /**
260      * Convert this value to its {@link String} representation.  Multiple
261      * values are separated by commas.
262      *
263      * @return Returns said string.
264      */
toStringImpl()265     protected final String toStringImpl() {
266         if ( m_value == null )
267             return null;
268         final StringBuilder sb = new StringBuilder();
269         boolean comma = false;
270         for ( Rational value : m_value ) {
271             if ( !comma )
272                 comma = true;
273             else
274                 sb.append( ',' );
275             sb.append( value.toString() );
276         }
277         return sb.toString();
278     }
279 
280     ////////// private ////////////////////////////////////////////////////////
281 
282     private Rational[] m_value;
283 }
284 /* vim:set et sw=4 ts=4: */
285