View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.datamodel.metadata;
20  
21  import java.math.BigDecimal;
22  import java.text.NumberFormat;
23  import java.util.Locale;
24  import java.util.Objects;
25  
26  import org.apache.logging.log4j.LogManager;
27  import org.apache.logging.log4j.Logger;
28  import org.jdom2.Element;
29  import org.mycore.common.MCRException;
30  import org.mycore.common.config.MCRConfiguration2;
31  
32  /**
33   * <p>
34   * Implements methods to handle MCRMetaNumber fields of a metadata object.
35   * The MCRMetaNumber class presents a number value in decimal
36   * format and optional a dimension type and a measurement. The input number can have the format
37   * like <em>xxxx</em>, <em>xxxx.x</em> or <em>xxxx,xxx</em>.
38   * </p>
39   * <p>
40   * The length of the dimension type is limited by the property <em>MCR.Metadata.MetaNumber.DimensionLength</em>.
41   * The default length is defined in MAX_DIMENSION_LENGTH = 128.
42   * </p>
43   * <p>
44   * The length of the measurement type is limited by the property <em>MCR.Metadata.MetaNumber.MeasurementLength</em>.
45   * The default length is defined in MAX_MEASURE_LENGTH = 64.
46   * </p>
47   * <p>
48   * The String output format of the number is determined by the property
49   * <em>MCR.Metadata.MetaNumber.FractionDigits</em>, default is DEFAULT_FRACTION_DIGITS = 3, and the ENGLISH Locale.
50   * For more digits in fraction as defined in property <em>MCR.Metadata.MetaNumber.FractionDigits</em> 
51   * will be round the output!
52   * </p>
53   * <p>
54   * For transforming the dot of the ENGLISH locale to other Characters use the tools of your layout process.
55   * </p>
56   * <pre>
57   * &lt;tag class="MCRMetaNumber" heritable="..."&gt;
58   *   &lt;subtag type="..." measurement="..." dimension="..."&gt;
59   *     xxxx.xxx or xxx
60   *   &lt;/subtag&gt;
61   * &lt;/tag&gt;
62   * </pre>
63   * 
64   * @author Jens Kupferschmidt
65   * @version $Revision$ $Date$
66   */
67  public final class MCRMetaNumber extends MCRMetaDefault {
68  
69      public static final int MAX_DIMENSION_LENGTH = 128;
70  
71      public static final int MAX_MEASUREMENT_LENGTH = 64;
72  
73      public static final int DEFAULT_FRACTION_DIGITS = 3;
74  
75      private BigDecimal number;
76  
77      private String dimension;
78  
79      private String measurement;
80  
81      private static final Logger LOGGER = LogManager.getLogger();
82  
83      private int fractionDigits;
84  
85      private int dimensionLength;
86  
87      private int measurementLength;
88  
89      private void loadProperties() {
90          fractionDigits = MCRConfiguration2.getInt("MCR.Metadata.MetaNumber.FractionDigits")
91              .orElse(DEFAULT_FRACTION_DIGITS);
92          dimensionLength = MCRConfiguration2.getInt("MCR.Metadata.MetaNumber.DimensionLength")
93              .orElse(MAX_DIMENSION_LENGTH);
94          measurementLength = MCRConfiguration2.getInt("MCR.Metadata.MetaNumber.MeasurementLength")
95              .orElse(MAX_MEASUREMENT_LENGTH);
96      }
97  
98      /**
99       * This is the constructor. <br>
100      * Sets the number to zero, the measurement and the dimension to an empty string.
101      */
102     public MCRMetaNumber() {
103         super();
104         loadProperties();
105         number = new BigDecimal("0");
106         dimension = "";
107         measurement = "";
108     }
109 
110     /**
111      * This is the constructor. <br>
112      * The subtag element was set to the value of <em>subtag</em>.
113      * If the value of <em>subtag</em>  is null or empty a MCRException will be thrown.
114      * The dimension element was set to the value of <em>dimension</em>, if it is null,
115      * an empty string was set to the dimension element.
116      * The measurement element was set to the value of <em>measurement</em>, if it is null,
117      * an empty string was set to the measurement element.
118      * The number string <em>number</em> was set to the number element, if it is null, empty or not a number, a
119      * MCRException will be thown.
120      * @param subtag       the name of the subtag
121      * @param inherted     a value &gt;= 0
122      * @param dimension    the optional dimension string
123      * @param measurement  the optional measurement string
124      * @param number       the number string
125      * @exception MCRException if the subtag value is null or empty or if
126      *   the number string is not in a number format
127      */
128     public MCRMetaNumber(String subtag, int inherted, String dimension, String measurement, String number)
129         throws MCRException {
130         super(subtag, null, null, inherted);
131         loadProperties();
132         setDimension(dimension);
133         setMeasurement(measurement);
134         setNumber(number);
135     }
136 
137     /**
138      * This is the constructor. <br>
139      * The subtag element was set to the value of <em>subtag</em>.
140      * If the value of <em>subtag</em>  is null or empty a MCRException will be thrown.
141      * The dimension element was set to the value of <em>dimension</em>, if it is null,
142      * an empty string was set to the dimension element.
143      * The measurement element was set to the value of <em>measurement</em>, if it is null,
144      * an empty string was set to the measurement element.
145      * The number <em>number</em> was set to the number element, if it is null a MCRException will be thrown.
146      * @param subtag       the name of the subtag
147      * @param inherted     a value &gt;= 0
148      * @param dimension    the optional dimension string
149      * @param measurement  the optional measurement string
150      * @param number       the number string
151      * @exception MCRException if the subtag value is null or empty or if
152      *   the number string is not in a number format
153      */
154     public MCRMetaNumber(String subtag, int inherted, String dimension, String measurement, BigDecimal number)
155         throws MCRException {
156         super(subtag, null, null, inherted);
157         loadProperties();
158         setDimension(dimension);
159         setMeasurement(measurement);
160         setNumber(number);
161     }
162 
163     /**
164      * This method set the dimension, if it is null, an empty string was set to
165      * the dimension element.
166      *
167      * @param dimension
168      *            the dimension string
169      */
170     public void setDimension(String dimension) {
171         if (dimension == null) {
172             this.dimension = "";
173         } else {
174             if (dimension.length() > dimensionLength) {
175                 this.dimension = dimension.substring(dimensionLength);
176                 LOGGER.warn("{}: dimension is too long: {}", getSubTag(), dimension.length());
177             } else {
178                 this.dimension = dimension;
179             }
180         }
181     }
182 
183     /**
184      * This method set the measurement, if it is null, an empty string was set
185      * to the measurement element.
186      *
187      * @param measurement
188      *            the measurement string
189      */
190     public void setMeasurement(String measurement) {
191         if (measurement == null) {
192             this.measurement = "";
193         } else {
194             if (measurement.length() > measurementLength) {
195                 this.measurement = measurement.substring(measurementLength);
196                 LOGGER.warn("{}: measurement is too long: {}", getSubTag(), measurement.length());
197             } else {
198                 this.measurement = measurement;
199             }
200         }
201     }
202 
203     /**
204      * This method set the number, if it is null or not a number, a MCRException
205      * will be throw.
206      *
207      * @param number
208      *            the number string
209      * @exception MCRException
210      *                if the number string is not in a number format
211      */
212     public void setNumber(String number) throws MCRException {
213         try {
214             if (number == null) {
215                 throw new MCRException("Number cannot be null");
216             }
217             String tmpNumber = number.replace(',', '.');
218             this.number = new BigDecimal(tmpNumber);
219         } catch (NumberFormatException e) {
220             throw new MCRException("The format of a number is invalid.");
221         }
222     }
223 
224     /**
225      * This method set the number, if it is null a MCRException will be throw.
226      *
227      * @param number
228      *            the number as BigDecimal
229      * @exception MCRException
230      *                if the number string is null
231      */
232     public void setNumber(BigDecimal number) throws MCRException {
233         if (number == null) {
234             throw new MCRException("Number cannot be null");
235         }
236         this.number = number;
237     }
238 
239     /**
240      * This method get the dimension element.
241      *
242      * @return the dimension String
243      */
244     public String getDimension() {
245         return dimension;
246     }
247 
248     /**
249      * This method get the measurement element.
250      *
251      * @return the measurement String
252      */
253     public String getMeasurement() {
254         return measurement;
255     }
256 
257     /**
258      * This method get the number element.
259      *
260      * @return the number as BigDecimal
261      */
262     public BigDecimal getNumberAsBigDecimal() {
263         return number;
264     }
265 
266     /**
267      * This method get the number element as formatted String. The number of
268      * fraction digits is defined by property <em>MCR.Metadata.MetaNumber.FractionDigits</em>.
269      * The default is 3 fraction digits.
270      *
271      * @return the number as formatted String
272      */
273     public String getNumberAsString() {
274         final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.ENGLISH);
275         numberFormat.setGroupingUsed(false);
276         numberFormat.setMaximumFractionDigits(fractionDigits);
277         numberFormat.setMinimumFractionDigits(fractionDigits);
278         return numberFormat.format(number);
279     }
280 
281     /**
282      * This method read the XML input stream part from a DOM part for the
283      * metadata of the document.
284      *
285      * @param element
286      *            a relevant JDOM element for the metadata
287      */
288     @Override
289     public void setFromDOM(Element element) {
290         super.setFromDOM(element);
291         setMeasurement(element.getAttributeValue("measurement"));
292         setDimension(element.getAttributeValue("dimension"));
293         setNumber(element.getTextTrim());
294     }
295 
296     /**
297      * This method creates an XML element containing all data in this instance,
298      * as defined by the MyCoRe XML MCRNumber definition for the given subtag.
299      *
300      * @exception MCRException
301      *                if the content of this instance is not valid
302      * @return a JDOM Element with the XML MCRNumber part
303      */
304     @Override
305     public Element createXML() throws MCRException {
306         Element elm = super.createXML();
307         if (dimension != null && dimension.length() != 0) {
308             elm.setAttribute("dimension", dimension);
309         }
310 
311         if (measurement != null && measurement.length() != 0) {
312             elm.setAttribute("measurement", measurement);
313         }
314         elm.addContent(getNumberAsString());
315         return elm;
316     }
317 
318     /**
319      * clone of this instance
320      * 
321      * you will get a (deep) clone of this element
322      * 
323      * @see java.lang.Object#clone()
324      */
325     @Override
326     public MCRMetaNumber clone() {
327         MCRMetaNumber clone = (MCRMetaNumber) super.clone();
328 
329         clone.dimension = this.dimension;
330         clone.measurement = this.measurement;
331         clone.number = this.number;
332 
333         return clone;
334     }
335 
336     /**
337      * Logs debug output.
338      */
339     @Override
340     public void debug() {
341         if (LOGGER.isDebugEnabled()) {
342             super.debugDefault();
343             LOGGER.debug("Measurement        = {}", measurement);
344             LOGGER.debug("Dimension          = {}", dimension);
345             LOGGER.debug("Value              = {}", number.toPlainString());
346             LOGGER.debug("");
347         }
348     }
349 
350     @Override
351     public boolean equals(Object obj) {
352         if (!super.equals(obj)) {
353             return false;
354         }
355         final MCRMetaNumber other = (MCRMetaNumber) obj;
356         return Objects.equals(measurement, other.measurement) && Objects.equals(dimension, other.dimension)
357             && Objects.equals(number, other.number);
358     }
359 
360 }