View Javadoc

1   package mork;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.Locale;
6   import java.util.regex.Matcher;
7   import java.util.regex.Pattern;
8   
9   /***
10   * A dictionary contains key/value pairs. The keys are used in data cells (e.g.
11   * Aliases Definitions) to compress data and reference the values stored in
12   * dictionaries.
13   * 
14   * Each dictionary can optionally be categorized into a scope. The default scope
15   * is the Atom Scope, which is used for literal values of actual content (in
16   * contrast to the Column Scope, which us used for header data only)
17   * 
18   * @author mhaller
19   */
20  public class Dict {
21  
22      /*** A typed empty list of dictionaries */
23      public static final List<Dict> EMPTY_LIST = new ArrayList<Dict>(0);
24  
25      /*** The name of the scope, e.g. 'a' or 'atomScope' */
26      private String scopeName;
27  
28      /*** The value of the scope, usually 'c' */
29      private String scopeValue;
30  
31      /***
32       * Internal reference to aliases which were included in the Dictionary
33       * definition
34       */
35      private Aliases aliases;
36  
37      /***
38       * Parse a Dictionary using the given String content. The simplest
39       * dictionary possible is <code>&gt;&lt;</code>.
40       * 
41       * @param dictString
42       *            a valid dictionary definition
43       */
44      public Dict(String dictString) {
45          this(dictString, Dict.EMPTY_LIST);
46      }
47  
48      /***
49       * Parse a Dictionary using the given String content. The simplest
50       * dictionary possible is <code>&gt;&lt;</code>.
51       * 
52       * @param dictString
53       *            a valid dictionary definition
54       * @param dicts
55       *            a preexisting list of dictionaries
56       */
57      public Dict(String dictString, List<Dict> dicts) {
58          // dictString = StringUtils.removeCommentLines(dictString);
59          // dictString = StringUtils.removeNewlines(dictString);
60  
61          Pattern pattern = 
62              Pattern.compile("//s*<//s*(<//(?.*//)?>)?[//s//n//r]*(.*)>[//s//r//n]*", 
63                              Pattern.MULTILINE);
64          Matcher matcher = pattern.matcher(dictString);
65          if (!matcher.find()) {
66              throw new RuntimeException("RegEx does not match: " + dictString);
67          }
68          String scopeDef = matcher.group(1);
69          String aliasesDef = matcher.group(2);
70  
71          // Scope
72          if (scopeDef != null) {
73              Pattern scopePattern = Pattern.compile("<//(?(.*)=([^//)])//)?>");
74              Matcher scopeMatcher = scopePattern.matcher(scopeDef);
75              if (scopeMatcher.matches()) {
76                  scopeName = scopeMatcher.group(1);
77                  scopeValue = scopeMatcher.group(2);
78              }
79          }
80  
81          // Aliases
82          aliases = new Aliases(aliasesDef, dicts);
83      }
84  
85      /***
86       * Returns the default scope of the parsed dictionary. This is not
87       * necessarily the same as the "global default scope", which is the Atom
88       * Scope.
89       * 
90       * Since Aliases itself could be scoped, a dictionary has a its own default
91       * scope for contained non-scoped aliases.
92       * 
93       * @return the default scope of the Dictionary, one of {@link ScopeTypes}
94       */
95      public ScopeTypes getDefaultScope() {
96          if (scopeValue != null && 
97              scopeValue.toLowerCase(Locale.getDefault()).startsWith("c")) {
98              return ScopeTypes.COLUMN_SCOPE;
99          }
100         return ScopeTypes.ATOM_SCOPE;
101     }
102 
103     /***
104      * Returns the name of the scope,if the Dictionary included a scope
105      * definition.
106      * 
107      * @return the name of the scope of the Dictionary, if there was any, or
108      *         <code>null</code>
109      */
110     public String getScopeName() {
111         return scopeName;
112     }
113 
114     /***
115      * Returns the value of the scope, if the Dictionary has any.
116      * 
117      * @return the value of the scope, or <code>null</code> if there was no
118      *         explicit scope definition.
119      */
120     public String getScopeValue() {
121         return scopeValue;
122     }
123 
124     /***
125      * Returns the value of a parsed alias, if the Dictionary declared it.
126      * 
127      * @param id
128      *            the Alias Key to dereference
129      * @return the value of the alias with the key id
130      */
131     public String getValue(String id) {
132         return aliases.getValue(id);
133     }
134 
135     /***
136      * Returns the number of aliases available in this Dictionary
137      * 
138      * @return the count number of aliases available
139      */
140     public int getAliasCount() {
141         return aliases.count();
142     }
143 
144     /***
145      * Dereferences a pointer to a value using this dictionary.
146      * 
147      * @param id
148      *            the id with the "^"-Prefix
149      */
150     public String dereference(String id) {
151         if (!id.startsWith("^")) {
152             throw new RuntimeException("dereference() must be called with a reference id including the prefix '^'");
153         }
154         String oid = id.substring(1);
155         String value = getValue(oid);
156         return value;
157     }
158 
159     /***
160      * Dereferences a pointer to a value using the given list of dictionaries to
161      * resolve it in the given scope.
162      * 
163      * @param id
164      *            the pointer id
165      * @param dicts
166      *            a list of dictionaries
167      * @param scope
168      *            the scope to look in
169      * @return the value if could be dereferenced
170      * @throws RuntimeException
171      *             if the dictionaries are empty or the value could not be found
172      */
173     public static String dereference(String id, List<Dict> dicts, 
174                                      ScopeTypes scope) {
175         if (dicts.isEmpty()) {
176             throw new RuntimeException("Cannot dereference IDs without dictionaries");
177         }
178         String dereference = null;
179         for (Dict dict: dicts) {
180             if (dict.getDefaultScope() == scope) {
181                 dereference = dict.dereference(id);
182                 if (dereference != null) {
183                     return dereference;
184                 } else {
185                 }
186             }
187         }
188         throw new RuntimeException("Dictionary could not dereference key: " + 
189                                    id + " in scope " + scope);
190     }
191 
192 }