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 }