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