Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/sun/xacml/attr/YearMonthDurationAttribute.java


1   
2   /*
3    * @(#)YearMonthDurationAttribute.java
4    *
5    * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions are met:
9    *
10   *   1. Redistribution of source code must retain the above copyright notice,
11   *      this list of conditions and the following disclaimer.
12   * 
13   *   2. Redistribution in binary form must reproduce the above copyright
14   *      notice, this list of conditions and the following disclaimer in the
15   *      documentation and/or other materials provided with the distribution.
16   *
17   * Neither the name of Sun Microsystems, Inc. or the names of contributors may
18   * be used to endorse or promote products derived from this software without
19   * specific prior written permission.
20   * 
21   * This software is provided "AS IS," without a warranty of any kind. ALL
22   * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
23   * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
24   * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
25   * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
26   * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
27   * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
28   * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
29   * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
30   * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
31   * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32   *
33   * You acknowledge that this software is not designed or intended for use in
34   * the design, construction, operation or maintenance of any nuclear facility.
35   */
36  
37  package com.sun.xacml.attr;
38  
39  import com.sun.xacml.ParsingException;
40  
41  import java.math.BigInteger;
42  
43  import java.net.URI;
44  
45  import java.util.regex.Pattern;
46  import java.util.regex.PatternSyntaxException;
47  import java.util.regex.Matcher;
48  
49  import org.w3c.dom.Node;
50  
51  
52  /**
53   * Representation of an xf:yearMonthDuration value. This class supports parsing
54   * xd:yearMonthDuration values. All objects of this class are immutable and
55   * thread-safe. The <code>Date</code> objects returned are not, but
56   * these objects are cloned before being returned.
57   * 
58   * @since 1.0
59   * @author Steve Hanna
60   */
61  public class YearMonthDurationAttribute extends AttributeValue
62  {
63      /**
64       * Official name of this type
65       */
66      public static final String identifier =
67          "http://www.w3.org/TR/2002/WD-xquery-operators-20020816#" +
68          "yearMonthDuration";
69   
70      /**
71       * URI version of name for this type
72       * <p>
73       * This field is initialized by a static initializer so that
74       * we can catch any exceptions thrown by URI(String) and
75       * transform them into a RuntimeException, since this should
76       * never happen but should be reported properly if it ever does.
77       */
78      private static URI identifierURI;
79  
80      /**
81       * RuntimeException that wraps an Exception thrown during the
82       * creation of identifierURI, null if none.
83       */
84      private static RuntimeException earlyException;
85  
86      /**
87       * Static initializer that initializes the identifierURI
88       * class field so that we can catch any exceptions thrown
89       * by URI(String) and transform them into a RuntimeException.
90       * Such exceptions should never happen but should be reported
91       * properly if they ever do.
92       */
93      static {
94          try {
95              identifierURI = new URI(identifier);
96          } catch (Exception e) {
97              earlyException = new IllegalArgumentException();
98              earlyException.initCause(e);
99          }
100     };
101 
102     /**
103      * Regular expression for yearMonthDuration (a la java.util.regex)
104      */
105     private static final String patternString = 
106         "(\\-)?P((\\d+)?Y)?((\\d+)?M)?";
107 
108     /**
109      * The index of the capturing group for the negative sign.
110      */
111     private static final int GROUP_SIGN = 1;
112 
113     /**
114      * The index of the capturing group for the number of years.
115      */
116     private static final int GROUP_YEARS = 3;
117 
118     /**
119      * The index of the capturing group for the number of months.
120      */
121     private static final int GROUP_MONTHS = 5;
122 
123     /**
124      * Static BigInteger values. We only use these if one of
125      * the components is bigger than Integer.MAX_LONG and we
126      * want to detect overflow, so we don't initialize these
127      * until they're needed.
128      */
129     private static BigInteger big12;
130     private static BigInteger bigMaxLong;
131 
132     /**
133      * A shared Pattern object, only initialized if needed
134      */
135     private static Pattern pattern;
136 
137     /**
138      * Negative flag. true if duration is negative, false otherwise
139      */
140     private boolean negative;
141 
142     /**
143      * Number of years
144      */
145     private long years;
146 
147     /**
148      * Number of months
149      */
150     private long months;
151 
152     /**
153      * Total number of months (used for equals)
154      */
155     private long totalMonths;
156 
157     /**
158      * Cached encoded value (null if not cached yet).
159      */
160     private String encodedValue = null;
161 
162     /**
163      * Creates a new <code>YearMonthDurationAttribute</code> that represents
164      * the duration supplied.
165      *
166      * @param negative true if the duration is negative, false otherwise
167      * @param years the number of years in the duration (must be positive)
168      * @param months the number of months in the duration (must be positive)
169      * @throws IllegalArgumentException if the total number of months
170      *                                  exceeds Long.MAX_LONG or the number
171      *                                  of months or years is negative
172      */
173     public YearMonthDurationAttribute(boolean negative, long years,
174                                       long months)
175         throws IllegalArgumentException {
176         super(identifierURI);
177 
178         // Shouldn't happen, but just in case...
179         if (earlyException != null)
180             throw earlyException;
181 
182         this.negative = negative;
183         this.years = years;
184         this.months = months;
185 
186         // Convert all the components except nanoseconds to milliseconds
187 
188         // If any of the components is big (too big to be an int),
189         // use the BigInteger class to do the math so we can detect
190         // overflow.
191         if ((years > Integer.MAX_VALUE) || (months > Integer.MAX_VALUE)) {
192             if (big12 == null) {
193                 big12 = BigInteger.valueOf(12);
194                 bigMaxLong = BigInteger.valueOf(Long.MAX_VALUE);
195             }
196             BigInteger bigMonths = BigInteger.valueOf(months);
197             BigInteger bigYears = BigInteger.valueOf(years);
198 
199             BigInteger bigTotal = bigYears.multiply(big12).add(bigMonths);
200 
201             // If the result is bigger than Long.MAX_VALUE, we have an
202             // overflow. Indicate an error (should be a processing error,
203             // since it can be argued that we should handle gigantic
204             // values for this).
205             if (bigTotal.compareTo(bigMaxLong) == 1)
206                 throw new IllegalArgumentException("total number of " +
207                                                    "months " +
208                                                    "exceeds Long.MAX_VALUE");
209             // If no overflow, convert to a long.
210             totalMonths = bigTotal.longValue();
211             if (negative)
212                 totalMonths = - totalMonths;
213         } else {
214             // The numbers are small, so do it the fast way.
215             totalMonths = ((years * 12) + months) * (negative ? -1 : 1);
216         }
217     }
218 
219     /**
220      * Returns a new <code>YearMonthDurationAttribute</code> that represents
221      * the xf:yearMonthDuration at a particular DOM node.
222      *
223      * @param root the <code>Node</code> that contains the desired value
224      * @return a new <code>YearMonthDurationAttribute</code> representing the
225      *         appropriate value
226      * @throws ParsingException if any problems occurred while parsing
227      */
228     public static YearMonthDurationAttribute getInstance(Node root)
229         throws ParsingException
230     {
231         return getInstance(root.getFirstChild().getNodeValue());
232     }
233 
234     /**
235      * Returns the long value for the capturing group groupNumber.
236      * This method takes a Matcher that has been used to match a
237      * Pattern against a String, fetches the value for the specified
238      * capturing group, converts that value to an long, and returns
239      * the value. If that group did not match, 0 is returned.
240      * If the matched value is not a valid long, NumberFormatException
241      * is thrown.
242      *
243      * @param matcher the Matcher from which to fetch the group
244      * @param groupNumber the group number to fetch
245      * @return the long value for that groupNumber
246      * @throws NumberFormatException if the string value for that
247      * groupNumber is not a valid long
248      */
249     private static long parseGroup(Matcher matcher, int groupNumber)
250         throws NumberFormatException {
251         long groupLong = 0;
252 
253         if (matcher.start(groupNumber) != -1) {
254             String groupString = matcher.group(groupNumber);
255             groupLong = Long.parseLong(groupString);
256         }
257         return groupLong;
258     }
259 
260     /**
261      * Returns a new <code>YearMonthDurationAttribute</code> that represents
262      * the xf:yearMonthDuration value indicated by the string provided.
263      *
264      * @param value a string representing the desired value
265      *
266      * @return a new <code>YearMonthDurationAttribute</code> representing the
267      *         desired value
268      *
269      * @throws ParsingException if any problems occurred while parsing
270      */
271     public static YearMonthDurationAttribute getInstance(String value)
272         throws ParsingException
273     {
274         boolean negative = false;
275         long years = 0;
276         long months = 0;
277 
278         // Compile the pattern, if not already done.
279         if (pattern == null) {
280             try {
281                 pattern = Pattern.compile(patternString);
282             } catch (PatternSyntaxException e) {
283                 // This should never happen
284                 throw new ParsingException("unexpected pattern syntax error");
285             }
286         }
287 
288         // See if the value matches the pattern.
289         Matcher matcher = pattern.matcher(value);
290         boolean matches = matcher.matches();
291 
292         // If not, syntax error!
293         if (!matches) {
294             throw new ParsingException("Syntax error in yearMonthDuration");
295         }
296 
297         // If the negative group matched, the value is negative.
298         if (matcher.start(GROUP_SIGN) != -1)
299             negative = true;
300 
301         try {
302             // If the years group matched, parse that value.
303             years = parseGroup(matcher, GROUP_YEARS);
304 
305             // If the months group matched, parse that value.
306             months = parseGroup(matcher, GROUP_MONTHS);
307         } catch (NumberFormatException e) {
308             // If we run into a number that's too big to be a long
309             // that's an error. Really, it's a processing error,
310             // since one can argue that we should handle that.
311             throw new ParsingException("Unable to handle number size");
312         }
313 
314         // If parsing went OK, create a new YearMonthDurationAttribute
315         // object and return it.
316         return new YearMonthDurationAttribute(negative, years, months);
317     }
318 
319     /**
320      * Returns true if the duration is negative.
321      *
322      * @return true if the duration is negative, false otherwise
323      */
324     public boolean isNegative() {
325         return negative;
326     }
327 
328     /**
329      * Gets the number of years.
330      *
331      * @return the number of years
332      */
333     public long getYears() {
334         return years;
335     }
336 
337     /**
338      * Gets the number of months.
339      *
340      * @return the number of months
341      */
342     public long getMonths() {
343         return months;
344     }
345 
346     /**
347      * Returns true if the input is an instance of this class and if its
348      * value equals the value contained in this class.
349      *
350      * @param o the object to compare
351      *
352      * @return true if this object and the input represent the same value
353      */
354     public boolean equals(Object o) {
355         if (! (o instanceof YearMonthDurationAttribute))
356             return false;
357 
358         YearMonthDurationAttribute other = (YearMonthDurationAttribute)o;
359 
360         return (totalMonths == other.totalMonths);
361     }
362 
363     /**
364      * Returns the hashcode value used to index and compare this object with
365      * others of the same type. Typically this is the hashcode of the backing
366      * data object.
367      *
368      * @return the object's hashcode value
369      */
370     public int hashCode() {
371         return (int) totalMonths ^ (int) (totalMonths >> 32);
372     }
373 
374     /**
375      * Converts to a String representation.
376      *
377      * @return the String representation
378      */
379     public String toString() {
380         StringBuffer sb = new StringBuffer();
381         sb.append("YearMonthDurationAttribute: [\n");
382         sb.append("  Negative: " + negative);
383         sb.append("  Years: " + years);
384         sb.append("  Months: " + months);
385         sb.append("]");
386 
387         return sb.toString();
388     }
389 
390     /**
391      * Encodes the value in a form suitable for including in XML data like
392      * a request or an obligation. This must return a value that could in
393      * turn be used by the factory to create a new instance with the same
394      * value.
395      *
396      * @return a <code>String</code> form of the value
397      */
398     public String encode() {
399         if (encodedValue != null)
400             return encodedValue;
401 
402         // Length is variable
403         StringBuffer buf = new StringBuffer(10);
404 
405         if (negative)
406             buf.append('-');
407         buf.append('P');
408         if ((years != 0) || (months == 0)) {
409             buf.append(Long.toString(years));
410             buf.append('Y');
411         }
412         if (months != 0) {
413             buf.append(Long.toString(months));
414             buf.append('M');
415         }
416 
417         encodedValue = buf.toString();
418 
419         return encodedValue;
420     }
421 }