001package conexp.fx.core.xml;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2023 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.io.BufferedWriter;
026import java.io.File;
027import java.io.FileWriter;
028import java.io.IOException;
029import java.util.Map;
030
031import org.jsoup.Jsoup;
032import org.jsoup.nodes.Document;
033import org.jsoup.nodes.Element;
034
035import conexp.fx.core.collections.Collections3;
036
037/**
038 * Use an XMLFile for persistent configuration values. Each value is adressed by a structured key. Keys must be
039 * lower-case strings (!!!) and are simply structured by dots, like URLs or names within ontologies.
040 */
041public final class XMLFile extends AbstractCompoundData {
042
043  private final File file;
044
045  public XMLFile(final File file) throws IOException {
046    super(Datatype.DOCUMENT, "", readElementFromFile(file), new Metadata(readElementFromFile(file)));
047    this.file = file;
048  }
049
050  public final File getFile() {
051    return file;
052  }
053
054  private synchronized static final Element readElementFromFile(final File file) throws IOException {
055    return Jsoup.parse(file, null).body();
056  }
057
058  public synchronized final void store() throws IOException {
059    writeDocumentToFile(writeDataToDocument());
060  }
061
062  private synchronized final Document writeDataToDocument() {
063    final Document document = new Document("");
064    writeDataToElement(value, document, document.appendElement("metadata"));
065    return document;
066  }
067
068  private synchronized final void
069      writeDataToElement(final Map<String, Data<?>> map, final Element element, final Element metadata) {
070    for (final String key : Collections3.sort(map.keySet())) {
071      final Data<?> value = map.get(key);
072      Element metadata_data = null;
073      if (metadata != null) {
074        metadata_data = metadata.appendElement("data");
075        metadata_data.appendElement("key").text(key);
076        metadata_data.appendElement("type").text(value.getType().toString());
077        switch (value.getType()) {
078        case BOOLEAN_LIST:
079        case INTEGER_LIST:
080        case FLOAT_LIST:
081        case STRING_LIST:
082        case COMPOUND_LIST:
083          JsoupUtil.firstOrAppendElement(metadata_data, "metadata", "subkey").text(value.toListData().getSubkey());
084        default:
085        }
086      }
087      final Element data = JsoupUtil.firstOrAppendElement(element, Key.toArray(key));
088      switch (value.getType()) {
089      case BOOLEAN:
090        data.text(value.toBooleanData().getValue().toString());
091        break;
092      case INTEGER:
093        data.text(value.toIntegerData().getValue().toString());
094        break;
095      case FLOAT:
096        data.text(value.toFloatData().getValue().toString());
097        break;
098      case STRING:
099        data.text(value.toStringData().getValue());
100        break;
101      case COMPOUND:
102        writeDataToElement(
103            value.toCompoundData(),
104            data,
105            metadata != null ? JsoupUtil.firstOrAppendElement(metadata_data, "metadata") : null);
106        break;
107      case BOOLEAN_LIST:
108        for (Boolean boolean_ : value.toBooleanListData())
109          data.appendElement(value.toListData().getSubkey()).text(boolean_.toString());
110        break;
111      case INTEGER_LIST:
112        for (Integer integer : value.toIntegerListData())
113          data.appendElement(value.toListData().getSubkey()).text(integer.toString());
114        break;
115      case FLOAT_LIST:
116        for (Float floating : value.toFloatListData())
117          data.appendElement(value.toListData().getSubkey()).text(floating.toString());
118        break;
119      case STRING_LIST:
120        for (String string : value.toStringListData())
121          data.appendElement(value.toListData().getSubkey()).text(string);
122        break;
123      case COMPOUND_LIST:
124        int i = 0;
125        for (Map<String, Data<?>> map_ : value.toCompoundListData())
126          writeDataToElement(
127              map_,
128              data.appendElement(value.toListData().getSubkey()),
129              i++ == 0 && metadata != null ? JsoupUtil.firstOrAppendElement(metadata_data, "metadata") : null);
130      case METADATA:
131      default:
132      }
133    }
134  }
135
136  private synchronized final void writeDocumentToFile(final Document document) throws IOException {
137    final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file));
138    bufferedWriter.append(document.toString());
139    bufferedWriter.close();
140  }
141
142  public static final void createEmptyConfiguration(File file) throws IOException {
143//              file.getParentFile().mkdirs();
144    file.createNewFile();
145    final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file));
146    bufferedWriter.append("<metadata>\r\n</metadata>");
147    bufferedWriter.close();
148  }
149}