View Javadoc
1   /*
2    * FIEBDC 3 parser  
3    * Copyright (C) 2014 DiSiD Technologies
4    *
5    * This program 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   * This program 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 this program.  If not, see <http://www.gnu.org/copyleft/gpl.html>.
17   */
18  
19  package com.disid.fiebdc3;
20  
21  import java.util.Date;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Map;
25  import java.util.Set;
26  
27  /**
28   * Contains the data of a Fiebdc 3 file.
29   * <p>
30   * A database has is composed of a root {@link Concept}, which itself might be
31   * composed of child {@link Concept}s:
32   * </p>
33   * 
34   * <pre>
35   * database
36   * '-> list of concepts
37   * '-> root concept breakdown (related to a concept)
38   *     |-> child concept breakdown 1 (related to a concept, might have chapter or workunit as children)
39   *     |   '-> grandson concept breakdown (related to a concept)
40   *     |       |-> measurement 1
41   *     |       |-> measurement 2
42   *     |       ...
43   *     |       '-> measurement n 
44   *     |-> child concept breakdown 2 (related to a concept)
45   *     ...
46   *     '-> child concept breakdown n (related to a concept)
47   * </pre>
48   * 
49   * <p>
50   * This class is also a factory for the elements it includes, so {@link Concept}
51   * s and {@link Measurement}s can only be created through a {@link Database}.
52   * </p>
53   * 
54   * @author DiSiD Team
55   */
56  public class Database {
57  
58      /**
59       * Enumeration of the available charset options for the Fiebdc3 file.
60       * 
61       * @author DiSiD Team
62       */
63      public enum Charset {
64          ANSI("ANSI"), C850("850"), C437("437");
65  
66          private final String code;
67  
68          /**
69           * Create a new Charset with the given code
70           * 
71           * @param code
72           *            of the charset
73           */
74          private Charset(String code) {
75              this.code = code;
76          }
77  
78          @Override
79          public String toString() {
80              return code;
81          }
82  
83          /**
84           * Returns the Charset with the given code or null if not available.
85           * 
86           * @param code
87           *            of the charset
88           * @return the Charset with the code
89           */
90          public static Charset parseCharset(String code) {
91              for (Charset c : Charset.values()) {
92                  if (c.code.equals(code)) {
93                      return c;
94                  }
95              }
96              return null;
97          }
98      }
99  
100     private String fileProperty;
101 
102     private String fileFormat;
103 
104     private String generatedBy;
105 
106     private String header;
107 
108     private Charset charset = Charset.C850;
109 
110     private String comments;
111 
112     private int infoType;
113 
114     private int certNum;
115 
116     private Date certDate;
117 
118     private ConceptBreakdown root;
119 
120     private Map<String, ConceptBreakdown> orphanBreakdowns = new HashMap<String, ConceptBreakdown>();
121 
122     private Set<Measurement> orphanMeasurements = new HashSet<Measurement>();
123 
124     private Map<String, Concept> concepts = new HashMap<String, Concept>();
125 
126     /**
127      * Spec definition:<br/>
128      * <i> PROPIEDAD_ARCHIVO: Redactor de la base de datos u obra, fecha, … </i>
129      * 
130      * @return the file writer or owner
131      */
132     public String getFileProperty() {
133         return fileProperty;
134     }
135 
136     public void setFileProperty(String fileProperty) {
137         this.fileProperty = fileProperty;
138     }
139 
140     /**
141      * Spec definition:<br/>
142      * <i> VERSION_FORMATO: VERSION del formato del archivo, la actual es
143      * FIEBDC-3/2007 </i>
144      */
145     public String getFileFormat() {
146         return fileFormat;
147     }
148 
149     public void setFileFormat(String fileFormat) {
150         this.fileFormat = fileFormat;
151     }
152 
153     /**
154      * Spec definition:<br/>
155      * <i> PROGRAMA_EMISION: Programa y/o empresa que genera los ficheros en
156      * formato BC3. </i>
157      */
158     public String getGeneratedBy() {
159         return generatedBy;
160     }
161 
162     public void setGeneratedBy(String generatedBy) {
163         this.generatedBy = generatedBy;
164     }
165 
166     /**
167      * Spec definition:<br/>
168      * <i> CABECERA: Título general de los ROTULOS_IDENTIFICACION. </i>
169      */
170     public String getHeader() {
171         return header;
172     }
173 
174     public void setHeader(String header) {
175         this.header = header;
176     }
177 
178     /**
179      * Spec definition:<br/>
180      * <i> JUEGO_CARACTERES: Asigna si el juego de caracteres a emplear es el
181      * definido para D.O.S., cuyos identificadores serán 850 ó 437, o es el
182      * definido para Windows, cuyo identificador será ANSI. En caso de que dicho
183      * campo esté vacío se interpretará, por omisión, que el juego de caracteres
184      * a utilizar será el 850 por compatibilidad con versiones anteriores. </i>
185      */
186     public Charset getCharset() {
187         return charset;
188     }
189 
190     public void setCharset(Charset charset) {
191         this.charset = charset;
192     }
193 
194     /**
195      * Spec definition:<br/>
196      * <i> COMENTARIO: Contenido del archivo (base, obra...). </i>
197      */
198     public String getComments() {
199         return comments;
200     }
201 
202     public void setComments(String comments) {
203         this.comments = comments;
204     }
205 
206     /**
207      * Spec definition:<br/>
208      * <i> TIPO INFORMACIÓN: Índice del tipo de información a intercambiar. Se
209      * definen los siguientes tipos: 1 Base de datos. 2 Presupuesto. 3
210      * Certificación (a origen). 4 Actualización de base de datos. </i>
211      */
212     public int getInfoType() {
213         return infoType;
214     }
215 
216     public void setInfoType(int infoType) {
217         this.infoType = infoType;
218     }
219 
220     /**
221      * Spec definition:<br/>
222      * <i> NÚMERO CERTIFICACIÓN: Valor numérico indicando el orden de la
223      * certificación (1, 2, 3,...) Solo tiene sentido cuando el tipo de
224      * información es Certificación. </i>
225      */
226     public int getCertNum() {
227         return certNum;
228     }
229 
230     public void setCertNum(int certNum) {
231         this.certNum = certNum;
232     }
233 
234     /**
235      * Spec definition:<br/>
236      * <i> FECHA CERTIFICACIÓN: Fecha de la certificación indicada en el campo
237      * número certificación. Solo tiene sentido cuando el tipo de información es
238      * Certificación. La fecha se definirá con el mismo formato que el campo
239      * DDMMAAAA de este registro </i>
240      */
241     public Date getCertDate() {
242         return certDate;
243     }
244 
245     public void setCertDate(Date certDate) {
246         this.certDate = certDate;
247     }
248 
249     /**
250      * Returns the database root {@link Concept}.
251      * 
252      * @return the main root concept
253      */
254     public ConceptBreakdown getRoot() {
255         return root;
256     }
257 
258     private void setRoot(ConceptBreakdown root) {
259         this.root = root;
260     }
261 
262     /**
263      * Creates and sets a new root concept breakdown for the database.
264      * 
265      * @param code
266      *            to identify the concept of the breakdown
267      * @return the new root concept breakdown
268      */
269     public ConceptBreakdown setRoot(String code) {
270         ConceptBreakdown bd = orphanBreakdowns.remove(code);
271         if (bd == null) {
272             bd = new ConceptBreakdown(code);
273         }
274         setRoot(bd);
275         return bd;
276     }
277 
278     public ConceptBreakdown addConceptBreakdown(String code) {
279         ConceptBreakdown bd = getExistingConceptBreakdown(code);
280         if (bd == null) {
281             bd = new ConceptBreakdown(code);
282             orphanBreakdowns.put(code, bd);
283         }
284         return bd;
285     }
286 
287     public ConceptBreakdown addConceptBreakdown(String parentCode, String code) {
288         if (parentCode == null) {
289             return addConceptBreakdown(code);
290         }
291         ConceptBreakdown parent = addConceptBreakdown(parentCode);
292         // Is already an organized ConceptBreakdown
293         ConceptBreakdown bd = parent.getCodeBreakdown(code);
294         // Is still an orphan breakdown
295         if (bd == null) {
296             bd = orphanBreakdowns.remove(code);
297         }
298         // It still does not exist
299         if (bd == null) {
300             bd = new ConceptBreakdown(parentCode, code);
301         }
302         parent.addChildBreakdownInfo(bd);
303         bd.setParentConceptCode(parentCode);
304         return bd;
305     }
306 
307     private ConceptBreakdown getExistingConceptBreakdown(String code) {
308         ConceptBreakdown bd = null;
309         if (getRoot() != null) {
310             bd = getRoot().getCodeBreakdown(code);
311         }
312 
313         if (bd == null) {
314             bd = orphanBreakdowns.get(code);
315         }
316 
317         return bd;
318     }
319 
320     /**
321      * Returns the concept with the given code in the database.
322      * 
323      * @param code
324      *            of the {@link Concept}
325      * @return the concept with the given code
326      */
327     public Concept getConcept(String code) {
328         return concepts.get(code);
329     }
330 
331     /**
332      * Returns the concept related to a concept breakdown.
333      * 
334      * @param bd
335      *            the concept breakdown
336      * @return the related concept
337      */
338     public Concept getConcept(ConceptBreakdown bd) {
339         return getConcept(bd.getConceptCode());
340     }
341 
342     /**
343      * Returns the concept with the given code in the database, or creates a new
344      * one if it didn't exist.
345      * 
346      * @param code
347      *            of the {@link Concept}
348      * @return the concept with the given code
349      */
350     public Concept getOrAddConcept(String code) {
351         Concept concept = getConcept(code);
352         return concept == null ? addConcept(code) : concept;
353     }
354 
355     /**
356      * Creates and adds a concept with the given code to the database.
357      * 
358      * @param code
359      *            of the {@link Concept}
360      * @return the new concept with the given code
361      */
362     private Concept addConcept(String code) {
363         Concept concept = new Concept(code);
364         concepts.put(code, concept);
365         return concept;
366     }
367 
368     /**
369      * Returns if there are any orphan concept breakdowns pending to be
370      * organized.
371      * 
372      * @return if there are any orphan concept breakdowns pending to be
373      *         organized
374      */
375     public boolean hasOrphanedConceptBreakdowns() {
376         return orphanBreakdowns != null && !orphanBreakdowns.isEmpty();
377     }
378 
379     /**
380      * Returns if there are any orphan measurements pending to be setted to a
381      * concept.
382      * 
383      * @return if there are any orphan measurements pending to be setted to a
384      *         concept
385      */
386     public boolean hasOrphanedMeasurements() {
387         return orphanMeasurements != null && !orphanMeasurements.isEmpty();
388     }
389 
390     /**
391      * Creates a new measurement and adds it to the database, without bein
392      * setted to a concept.
393      * 
394      * @return the new Measurement
395      */
396     public Measurement createMeasurement() {
397         Measurement measurement = new Measurement();
398         orphanMeasurements.add(measurement);
399         return measurement;
400     }
401 
402     /**
403      * Sets a Measurement to the related Concept.
404      * 
405      * @param conceptCode
406      *            code of the Concept to set the Measure to
407      * @param measurement
408      *            to set to the Concept
409      */
410     public void setMeasurement(String conceptCode, Measurement measurement) {
411         ConceptBreakdown bd = addConceptBreakdown(
412                 measurement.getParentConcept(), conceptCode);
413         bd.setMeasurement(measurement);
414         orphanMeasurements.remove(measurement);
415     }
416 
417     @Override
418     public String toString() {
419         return "Fiebdc3Database {" + "File property of: " + fileProperty
420                 + ", Format version: " + fileFormat + ", Generated by: "
421                 + generatedBy + ", Header: " + header + ", Charset: " + charset
422                 + ", Comments: " + comments + ", Information type: " + infoType
423                 + ", Certification number: " + certNum
424                 + ", Certification date: " + certDate + ", Concepts: "
425                 + concepts + ", Root Concept Breakdown: \n\t" + root
426                 + "\n, Orphan Breakdowns: " + orphanBreakdowns
427                 + ", Orphan Measurements: " + orphanMeasurements + "}";
428     }
429 }