View Javadoc
1   /*
2    * Copyright 2013-2023 Medical Information Systems Research Group (https://medical.zcu.cz),
3    * Department of Computer Science and Engineering, University of West Bohemia.
4    * Address: Univerzitni 8, 306 14 Plzen, Czech Republic.
5    *
6    * This file is part of Sparkle project.
7    *
8    * Sparkle is free software: you can redistribute it and/or modify
9    * it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License.
11   *
12   * Sparkle is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with Sparkle. If not, see <http://www.gnu.org/licenses/>.
19   */
20  package cz.zcu.mre.sparkle.data;
21  
22  import cz.zcu.mre.sparkle.Messages;
23  import cz.zcu.mre.sparkle.gui.text_editor.ITextEditor;
24  import cz.zcu.mre.sparkle.gui.text_editor.SyntaxValidator;
25  import cz.zcu.mre.sparkle.tools.Changeable;
26  import cz.zcu.mre.sparkle.tools.Definitions;
27  import cz.zcu.mre.sparkle.tools.sparqlValidation.SparqlValidationUtils;
28  import javafx.beans.InvalidationListener;
29  import javafx.beans.Observable;
30  import javafx.collections.FXCollections;
31  import javafx.collections.ObservableList;
32  import javafx.collections.ObservableSet;
33  import org.antlr.v4.runtime.Token;
34  import org.antlr.v4.runtime.Vocabulary;
35  import java.io.IOException;
36  import java.util.*;
37  import java.util.stream.Collectors;
38  
39  /**
40   * Třída pro skladování SPARQL funkcí.
41   *
42   * @author Jan Smucr
43   * @author Petr Vcelak (vcelak@kiv.zcu.cz)
44   * @author Klara Hlavacova
45   * @see AbstractTermsStorage
46   */
47  public final class FunctionsStorage
48          extends AbstractTermsStorage<StorageEntry>
49          implements Observable, Changeable {
50  
51      private final Set<InvalidationListener> observers = new HashSet<>();
52  
53      private static final long serialVersionUID = 3980575539821734838L;
54      private static final String XML_ELEMENT_NAME = "Functions";//$NON-NLS-1$
55  
56      private static final ObservableSet<FunctionEntry> BUILT_IN_FUNCTIONS = FXCollections.observableSet();
57      /**
58       * Typy terminalnich symbolu v SPARQL gramatice
59       */
60      private static final Vocabulary lexerVocabulary = SparqlValidationUtils.getLexerVocabulary();
61  
62      public FunctionsStorage(final DataAgent dataAgent, final PrefixesStorage functionPrefixesStorage) {
63          super(dataAgent, functionPrefixesStorage);
64          watch(super.getDataAgent());
65  
66          if (BUILT_IN_FUNCTIONS.isEmpty()) {
67              getBuiltinFunctions();
68          }
69  
70      }
71  
72      /**
73       * Deserializace objektu
74       *
75       * @param in vstupni proud
76       *
77       * @throws IOException IO exception.
78       * @throws ClassNotFoundException Class not found exception.
79       */
80      private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
81          in.defaultReadObject();
82          getBuiltinFunctions();
83      }
84  
85      private void getBuiltinFunctions() {
86          List<FunctionEntry> keywords = getKeywords();
87  
88          keywords.forEach((keyword) -> {
89              ArrayList<Token> tokens = SyntaxValidator.getTokens(keyword.getName());
90              String symbolName = lexerVocabulary.getSymbolicName(tokens.get(0).getType());
91              String tokenType = Messages.getString(symbolName, Messages.TOKEN_BUNDLE_NAME);
92              if (tokenType.equals(SparqlValidationUtils.FUNCTION_TOKEN_TYPE)
93                      || tokenType.equals(SparqlValidationUtils.AGGREGATES_TOKEN_TYPE)) {
94                  BUILT_IN_FUNCTIONS.add(new FunctionEntry(keyword.getName(), keyword.getTitle()));
95              }
96          });
97  
98      }
99  
100     /**
101      * Nacte klicova slova ze souboru
102      *
103      * @return seznam klicovych slov
104      */
105     private List<FunctionEntry> getKeywords() {
106         Properties props = Messages.getProperties(
107                 ITextEditor.class.getResourceAsStream(Messages.SPARQL_KEYWORDS_PROPERTIES));
108 
109         List<FunctionEntry> keywords = new ArrayList<>();
110         props.keySet().stream().filter((key) -> !(((String) key).length() < 1)).map((key) -> props.getProperty((String) key).split(Definitions.SEMICOLON)).filter((property) -> !(property.length == 0)).forEachOrdered((property) -> {
111             String functionName = property[0].replaceAll("\\([ ]*\\)", "");
112             String functionTitle = property.length > 1 ? property[1] : "";
113 
114             keywords.add(new FunctionEntry(functionName, functionTitle));
115         });
116         return keywords;
117     }
118 
119     /**
120      * @param dataAgent Instance {@link DataAgent}.
121      * @param functionPrefixesStorage Instance {@link PrefixesStorage}.
122      *
123      * @return Seznam vestavěných funkcí daný standardem SPARQL 1.1. Seznam
124      * neobsahuje funkce agregační.
125      */
126     public static final FunctionsStorage getBuiltIn11(final DataAgent dataAgent,
127             final PrefixesStorage functionPrefixesStorage) {
128 
129         return getBuiltIn(dataAgent, functionPrefixesStorage);
130     }
131 
132     /**
133      * @param dataAgent Instance {@link DataAgent}.
134      * @param functionPrefixesStorage Instance {@link PrefixesStorage}.
135      *
136      * @return Instance třídy {@link FunctionsStorage}.
137      */
138     private static FunctionsStorage getBuiltIn(final DataAgent dataAgent,
139             final PrefixesStorage functionPrefixesStorage) {
140 
141         final FunctionsStorage result = new FunctionsStorage(dataAgent, functionPrefixesStorage);
142         FunctionsStorage.BUILT_IN_FUNCTIONS.forEach(
143                 (function) -> result.addTerm(new BuiltInFunctionStorageEntry(function.getName(), function.getTitle())));
144 
145         return result;
146     }
147 
148     /**
149      * Vrací názvy funkcí dle prefixu.
150      *
151      * @param prefix Prefix.
152      * @param includePrefix Pokud <code>true</code>, prefix bude součástí
153      * vrácených názvů.
154      *
155      * @return Seznam názvů funkcí.
156      */
157     public final ObservableList<String> getFunctionsNamesByPrefix(final String prefix, final boolean includePrefix) {
158         return getTermsNamesByPrefix(prefix, includePrefix, StorageEntry.class);
159     }
160 
161     /**
162      * @return IRI všech funkcí, ve zkrácené nebo nezkrácené podobě.
163      */
164     public final ObservableList<Object> getAllFunctions() {
165         final ObservableList<Object> result = FXCollections.observableArrayList();
166         final PrefixesStorage prefixesStorage = getPrefixesStorage();
167 
168         getNamespaceToTermsMap().forEach((key, value) -> value.forEach((storageEntry) -> {
169             if (storageEntry instanceof BuiltInFunctionStorageEntry) {
170                 result.add(storageEntry.getIRI());
171             } else {
172                 final String s = storageEntry.getShortVariant(prefixesStorage);
173                 result.add(s == null ? storageEntry.getWrappedIRI() : s);
174             }
175         }));
176 
177         return result;
178     }
179 
180     /**
181      * @return Seznam IRI funkcí, které nelze zkrátit použitím prefixů.
182      */
183     public final ObservableList<Object> getUnprefixedFunctionNames() {
184 
185         final Set<StorageEntry> entries = getNamespaceToTermsMap().get("");
186         if (entries == null) {
187             return FXCollections.emptyObservableList();
188         }
189 
190         final ObservableList<Object> result = FXCollections.observableArrayList();
191         entries.forEach((storageEntry) -> {
192             if (storageEntry instanceof BuiltInFunctionStorageEntry) {
193                 result.add(new StorageEntry(storageEntry));
194             } else {
195                 result.add(storageEntry.getWrappedIRI());
196             }
197         });
198 
199         return result;
200     }
201 
202     @Override
203     public final String getTermNamespace(final StorageEntry term) {
204         return term.getNamespace();
205     }
206 
207     @Override
208     public final String getTermName(final StorageEntry term) {
209         return term.getLocalName();
210     }
211 
212     @Override
213     public String getTermTitle(StorageEntry term) {
214         return term.getTitle();
215     }
216 
217     /**
218      * @return Seznam IRI funkcí bez funkcí vestavěných.
219      */
220     public final ObservableList<String> getCustomFunctions() {
221         final ObservableList<Object> result = getAllFunctions();
222         result.removeIf(this::functionCompare);
223 
224         return getStringList(result);
225     }
226 
227     /**
228      * Vrati {@code true} pokud je funkce vestavena
229      *
230      * @param obj retezec nebo instance {@code BuiltInFunctionStorageEntry}
231      *
232      * @return {@code true} - funkce je vestavena
233      */
234     private boolean functionCompare(Object obj) {
235 
236         if (obj instanceof BuiltInFunctionStorageEntry) {
237             return BUILT_IN_FUNCTIONS.stream().anyMatch(
238                     f -> f.getName().equalsIgnoreCase(((BuiltInFunctionStorageEntry) obj).getIRI()));
239         } else {
240             return BUILT_IN_FUNCTIONS.stream().anyMatch(f -> f.getName().equalsIgnoreCase((String) obj));
241 
242         }
243     }
244 
245     @Override
246     protected final StorageEntry createTerm(final String namespace, final String localName, String title) {
247         return createTerm(namespace, localName, title, "");
248     }
249 
250     @Override
251     protected final StorageEntry createTerm(final String namespace, final String localName, String title, String type) {
252         return new StorageEntry(namespace, localName, title);
253     }
254 
255     @Override
256     public final String getXMLElementName() {
257         return XML_ELEMENT_NAME;
258     }
259 
260     @Override
261     public Set<InvalidationListener> getObservers() {
262         return observers;
263     }
264 
265     /**
266      * Prevede seznam retezecu retezecu reprezentovanych jako {@code Object} na
267      * seznam retezcu
268      *
269      * @param list seznam objektu (retezcu)
270      *
271      * @return seznam retezecu retezecu reprezentovanych jako {@code Object} na
272      * seznam retezcu
273      */
274     private ObservableList<String> getStringList(ObservableList<Object> list) {
275         ObservableList<String> objects = FXCollections.observableArrayList();
276         objects.addAll(list.stream().map(String::valueOf).collect(Collectors.toList()));
277 
278         return objects;
279     }
280 
281     /**
282      * Funkce - jmeno, popis
283      */
284     private static class FunctionEntry {
285 
286         private final String name;
287         private final String title;
288 
289         FunctionEntry(String name, String title) {
290             this.name = name;
291             this.title = title;
292         }
293 
294         String getName() {
295             return name;
296         }
297 
298         String getTitle() {
299             return title;
300         }
301     }
302 }