View Javadoc
1   /*
2    * Themis: Java Project Framework
3    * Copyright 2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  
18  package io.github.tonywasher.joceanus.themis.parser.maven;
19  
20  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataMap;
21  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
22  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
23  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
24  import io.github.tonywasher.joceanus.oceanus.base.OceanusSystem;
25  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
26  import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
27  import io.github.tonywasher.joceanus.themis.parser.base.ThemisDataResource;
28  import io.github.tonywasher.joceanus.themis.parser.maven.ThemisMavenId.ThemisXElementParser;
29  import org.w3c.dom.Element;
30  import org.w3c.dom.Node;
31  
32  import java.util.HashMap;
33  import java.util.Map;
34  
35  /**
36   * Maven property cache.
37   */
38  public class ThemisMavenPropertyCache
39          implements ThemisXElementParser, MetisDataMap<String, String>, MetisFieldItem {
40      /**
41       * Report fields.
42       */
43      private static final MetisFieldSet<ThemisMavenPropertyCache> FIELD_DEFS = MetisFieldSet.newFieldSet(ThemisMavenPropertyCache.class);
44  
45      /*
46       * Declare Fields.
47       */
48      static {
49          FIELD_DEFS.declareLocalField(ThemisDataResource.DATA_PARENT, ThemisMavenPropertyCache::getParent);
50      }
51  
52      /**
53       * The property prefix.
54       */
55      private static final String PROP_START = "${";
56  
57      /**
58       * The property suffix.
59       */
60      private static final String PROP_END = "}";
61  
62      /**
63       * The properties.
64       */
65      private final Map<String, String> theMap;
66  
67      /**
68       * The parent cache.
69       */
70      private ThemisMavenPropertyCache theParent;
71  
72      /**
73       * Constructor.
74       */
75      ThemisMavenPropertyCache() {
76          theMap = new HashMap<>();
77          setProperty("javafx.platform", OceanusSystem.determineSystem().getClassifier());
78      }
79  
80      @Override
81      public MetisFieldSet<ThemisMavenPropertyCache> getDataFieldSet() {
82          return FIELD_DEFS;
83      }
84  
85      @Override
86      public String formatObject(final OceanusDataFormatter pFormatter) {
87          return getClass().getSimpleName();
88      }
89  
90      @Override
91      public Map<String, String> getUnderlyingMap() {
92          return theMap;
93      }
94  
95      /**
96       * Obtain the Parent.
97       *
98       * @return the parent
99       */
100     private ThemisMavenPropertyCache getParent() {
101         return theParent;
102     }
103 
104     /**
105      * Set the parent cache.
106      *
107      * @param pParent the parent cache
108      */
109     void setParent(final ThemisMavenPropertyCache pParent) {
110         theParent = pParent;
111     }
112 
113     /**
114      * Set the property.
115      *
116      * @param pName  the name of the property
117      * @param pValue the value of the property
118      */
119     void setProperty(final String pName,
120                      final String pValue) {
121         final String myProperty = getPropertyKey(pName);
122         theMap.put(myProperty, pValue);
123     }
124 
125     /**
126      * Get the property.
127      *
128      * @param pName the name of the property
129      * @return the value of the property (or null)
130      */
131     String getProperty(final String pName) {
132         final String myProperty = getPropertyKey(pName);
133         return theMap.get(myProperty);
134     }
135 
136     /**
137      * Obtain the property key.
138      *
139      * @param pName the name of the property
140      * @return the property key
141      */
142     private String getPropertyKey(final String pName) {
143         return PROP_START + pName + PROP_END;
144     }
145 
146     @Override
147     public String getElementValue(final Element pElement,
148                                   final String pValue) throws OceanusException {
149         /* Return null if no element */
150         if (pElement == null) {
151             return null;
152         }
153 
154         /* Loop through the children */
155         for (Node myChild = pElement.getFirstChild();
156              myChild != null;
157              myChild = myChild.getNextSibling()) {
158             /* Return result if we have a match */
159             if (myChild instanceof Element
160                     && pValue.equals(myChild.getNodeName())) {
161                 return replaceProperty(myChild.getTextContent());
162             }
163         }
164 
165         /* Not found */
166         return null;
167     }
168 
169     /**
170      * Replace properties.
171      *
172      * @param pValue the value
173      * @return the value or the replaced property
174      */
175     private String replaceProperty(final String pValue) throws ThemisDataException {
176         /* Loop while there is a property to replace */
177         String myCurrent = pValue;
178         while (containsProperty(myCurrent)) {
179             /* Replace the next property */
180             final String myResult = replaceNextProperty(myCurrent);
181 
182             /* If we failed to change the property */
183             if (myResult.equals(myCurrent)) {
184                 throw new ThemisDataException("Unknown embedded property - " + myCurrent);
185             }
186 
187             /* Switch result */
188             myCurrent = myResult;
189         }
190         return myCurrent;
191     }
192 
193     /**
194      * Does the value contain a property?
195      *
196      * @param pValue the value
197      * @return true/false
198      */
199     private boolean containsProperty(final String pValue) {
200         final int myIndex = pValue.indexOf(PROP_START);
201         return myIndex != -1;
202     }
203 
204     /**
205      * Replace next property.
206      *
207      * @param pValue the value
208      * @return the value or the replaced property
209      */
210     private String replaceNextProperty(final String pValue) {
211         for (Map.Entry<String, String> myEntry : theMap.entrySet()) {
212             if (pValue.contains(myEntry.getKey())) {
213                 return pValue.replace(myEntry.getKey(), myEntry.getValue());
214             }
215         }
216         return theParent != null ? theParent.replaceNextProperty(pValue) : pValue;
217     }
218 }