View Javadoc
1   package org.djunits.unit;
2   
3   import java.io.Serializable;
4   import java.util.EnumMap;
5   
6   /**
7    * <p>
8    * Copyright (c) 2015-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
9    * BSD-style license. See <a href="http://djunits.org/docs/license.html">DJUNITS License</a>.
10   * <p>
11   * $LastChangedDate: 2019-04-01 02:14:38 +0200 (Mon, 01 Apr 2019) $, @version $Revision: 370 $, by $Author: averbraeck $,
12   * initial version Jun 15, 2014 <br>
13   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
14   */
15  public class SICoefficients implements Serializable
16  {
17      /** */
18      private static final long serialVersionUID = 1L;
19      
20      /** the map with SI base units and corresponding coefficients. */
21      private final EnumMap<SI, Integer> coefficientsMap;
22  
23      /**
24       * Construct an instance of SICoefficients.
25       * @param coefficients EnumMap&lt;SI,Integer&gt;; the map with SI base units and corresponding coefficients
26       */
27      protected SICoefficients(final EnumMap<SI, Integer> coefficients)
28      {
29          this.coefficientsMap = coefficients;
30      }
31  
32      /** {@inheritDoc} */
33      @Override
34      public final String toString()
35      {
36          return enumMapToString(this.coefficientsMap);
37      }
38  
39      /**
40       * Convert an enumMap of coefficient to the normalized string representation.
41       * @param map EnumMap&lt;{@link SI}, Integer&gt;; the EnumMap
42       * @return String
43       */
44      protected static String enumMapToString(final EnumMap<SI, Integer> map)
45      {
46          StringBuffer result = new StringBuffer();
47          for (SI si : map.keySet())
48          {
49              if (map.get(si) > 0)
50              {
51                  result.append(si.name());
52                  if (map.get(si) != 1)
53                  {
54                      result.append(map.get(si));
55                  }
56              }
57          }
58  
59          if (result.length() == 0)
60          {
61              result.append("1");
62          }
63  
64          boolean first = true;
65          for (SI si : map.keySet())
66          {
67              if (map.get(si) < 0)
68              {
69                  if (first)
70                  {
71                      result.append("/");
72                      first = false;
73                  }
74                  result.append(si.name());
75                  if (map.get(si) != -1)
76                  {
77                      result.append(-map.get(si));
78                  }
79              }
80          }
81          return result.toString();
82      }
83  
84      /**
85       * @return coefficientsMap
86       */
87      public final EnumMap<SI, Integer> getCoefficientsMap()
88      {
89          return this.coefficientsMap;
90      }
91  
92      /**
93       * Convert a coefficient string to <i>standard format</i>.
94       * @param coefficientString String; the string to convert
95       * @return String; the normalized coefficient string
96       * @throws UnitException when the coefficientString could not be parsed
97       */
98      public static String normalize(final String coefficientString) throws UnitException
99      {
100         return enumMapToString(parse(coefficientString));
101     }
102 
103     /**
104      * @param coefficientString String; such as kgm/s2 or kg-2m^3/s2A or Kmmol3/Askcd4 or mol. &lt;br&gt; The grammar of a
105      *            coefficientString is:<br>
106      *            <table summary="">
107      *            <tr>
108      *            <td>coefficientString</td>
109      *            <td>::=</td>
110      *            <td>&lt;empty&gt; | [ 1 | powerString ] | [ '1 /' powerString ]</td>
111      *            </tr>
112      *            <tr>
113      *            <td>powerString</td>
114      *            <td>::=</td>
115      *            <td>unitName [ [ ^ ] integer ] [ [ dotOrSlash ] powerString ]</td>
116      *            </tr>
117      *            <tr>
118      *            <td>dotOrSlash</td>
119      *            <td>::=</td>
120      *            <td>. | /</td>
121      *            </tr>
122      *            <tr>
123      *            <td>unitName</td>
124      *            <td>::=</td>
125      *            <td>kg | m | s | A | K | cd | mol</td>
126      *            </tr>
127      *            </table>
128      *            <br>
129      *            White space can appear anywhere in a coefficientString. <br>
130      *            If "integer" does not fit in an Integer, the resulting coefficient will be very wrong.
131      * @return an instance of SICoefficients
132      * @throws UnitException if the coefficientString is not parsable.
133      */
134     public static EnumMap<SI, Integer> parse(final String coefficientString) throws UnitException
135     {
136         // System.out.println("coefficientString is \"" + coefficientString + "\"");
137         EnumMap<SI, Integer> coefficients = new EnumMap<SI, Integer>(SI.class);
138         String cs = coefficientString;
139         cs = cs.replace(".", "").replace(" ", "");
140         if (cs.equals("1")) // This is a special case...
141         {
142             return coefficients;
143         }
144         if (cs.startsWith("1/"))
145         {
146             cs = cs.substring(1); // remove the leading "1"
147         }
148         int factor = 1;
149         while (cs.length() > 0)
150         {
151             if (cs.startsWith("/"))
152             {
153                 cs = cs.substring(1);
154                 if (cs.length() < 1)
155                 {
156                     throw new UnitException("No SI name after slash in " + coefficientString);
157                 }
158                 factor = -1;
159             }
160             boolean parsedPowerString = false;
161             int factor2 = 1;
162             for (SI si : SI.values())
163             {
164                 factor2 = 1;
165                 String name = si.name();
166                 if (!cs.startsWith(name))
167                 {
168                     continue;
169                 }
170                 int endPos = name.length();
171                 if (cs.substring(endPos).startsWith("ol"))
172                 {
173                     continue; // Don't confuse "m" (for meter) and "mol"
174                 }
175                 // Found the unit name
176                 if (cs.substring(endPos).startsWith("^"))
177                 {
178                     endPos++;
179                 }
180                 int value = 1;
181                 int digitsSeen = 0;
182                 if (cs.substring(endPos).startsWith("-"))
183                 {
184                     factor2 = -1;
185                     endPos++;
186                 }
187                 while (cs.length() > endPos)
188                 {
189                     char digit = cs.charAt(endPos);
190                     if (digit >= '0' && digit <= '9')
191                     {
192                         if (0 == digitsSeen)
193                         {
194                             value = 0;
195                         }
196                         value = value * 10 + digit - '0';
197                         endPos++;
198                         digitsSeen++;
199                     }
200                     else
201                     {
202                         break;
203                     }
204                 }
205                 Integer oldValue = coefficients.get(si);
206                 if (null == oldValue)
207                 {
208                     oldValue = 0;
209                 }
210                 coefficients.put(si, oldValue + value * factor * factor2);
211                 parsedPowerString = true;
212                 cs = cs.substring(endPos);
213                 break;
214             }
215             if (!parsedPowerString)
216             {
217                 throw new UnitException("Not an SI unit name in \"" + coefficientString + "\" at \"" + cs + "\"");
218             }
219         }
220         return coefficients;
221     }
222 
223     /**
224      * @param a SICoefficients; the first set of coefficients
225      * @param b SICoefficients; the second set of coefficients
226      * @return the coefficients of a*b (coefficients are added)
227      */
228     public static SICoefficients multiply(final SICoefficients a, final SICoefficients b)
229     {
230         EnumMap<SI, Integer> coefficients = new EnumMap<SI, Integer>(SI.class);
231         for (SI si : a.getCoefficientsMap().keySet())
232         {
233             coefficients.put(si, a.getCoefficientsMap().get(si));
234         }
235 
236         for (SI si : b.getCoefficientsMap().keySet())
237         {
238             if (coefficients.containsKey(si))
239             {
240                 coefficients.put(si, coefficients.get(si) + b.getCoefficientsMap().get(si));
241             }
242             else
243             {
244                 coefficients.put(si, b.getCoefficientsMap().get(si));
245             }
246         }
247 
248         for (SI si : coefficients.keySet())
249         {
250             if (coefficients.get(si) == 0)
251             {
252                 coefficients.remove(si);
253             }
254         }
255         return new SICoefficients(coefficients);
256     }
257 
258     /**
259      * @param a SICoefficients; the first set of coefficients
260      * @param b SICoefficients; the second set of coefficients
261      * @return the coefficients of a/b (coefficients are subtracted)
262      */
263     public static SICoefficients divide(final SICoefficients a, final SICoefficients b)
264     {
265         EnumMap<SI, Integer> coefficients = new EnumMap<SI, Integer>(SI.class);
266         for (SI si : a.getCoefficientsMap().keySet())
267         {
268             coefficients.put(si, a.getCoefficientsMap().get(si));
269         }
270 
271         for (SI si : b.getCoefficientsMap().keySet())
272         {
273             if (coefficients.containsKey(si))
274             {
275                 coefficients.put(si, coefficients.get(si) - b.getCoefficientsMap().get(si));
276             }
277             else
278             {
279                 coefficients.put(si, -b.getCoefficientsMap().get(si));
280             }
281         }
282 
283         for (SI si : coefficients.keySet())
284         {
285             if (coefficients.get(si) == 0)
286             {
287                 coefficients.remove(si);
288             }
289         }
290         return new SICoefficients(coefficients);
291     }
292 
293 }