-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathModule.java
213 lines (191 loc) · 5.96 KB
/
Module.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.function.Function;
public abstract class Module implements Cloneable
{
private boolean cloned = false;
private void protect()
{
/*
* This is to protect the predefined reusable modules in ModuleFactory
* from inadvertent changes to their states by ModuleFactory developers.
* The intent is to always keep the predefined modules unchanged (as templates)
* , so they will be safely shared (optionally registered multiple times to different menu items)
* , while TextParser.ModuleRegistrant class will perform a deep-clone of the Module object
* during every module registration and prior to every replacement cycle.
* The deep-cloned copy of the Module can then be successfully changed
* without throwing the following exception.
*
* List of methods that are protected against changes to un-cloned class instances:
* 1. setPromptDisplayMethod
*
* It is preferred to keep the Module class design at the least possible modifiability.
*
* It is also important to note that since the Module is cloned on-the-fly before every replacement cycle
* , any Module instance variables that are defined within ModuleFactory or TextParser classes will not carry over to the clone copy.
* This was done to safeguard the module by preventing instance variables that are created by ModuleFactory developer
* from carrying over to subsequent executions (replacements)
*
* Any new instance variables that need to be survive through cloning should be copied within the clone method.
*
* */
if(!cloned)
{
throw new TextParserException("INTERNAL: Attempting to modify a protected Module instance");
}
}
public Object clone() throws CloneNotSupportedException
{
Module o = (Module)super.clone();
o.displayMethod = this.displayMethod;
o.listOfReplaceables = (ArrayList<Replaceable>)(this.listOfReplaceables);
o.cloned = true;
return o;
}
interface Displayable
{
/*
* Both Module and JFrame (Component) are passed to display method
* , since it controls both GUI and module logic.
* */
public ModuleContext display(Module module, TextParser parent);
}
private Displayable displayMethod = null;
public boolean isPromptDisplayEnabled()
{
if(displayMethod == null)
return false;
else
return true;
}
public ModuleContext initContext()
{
return new ModuleContext();
}
public class ModuleContext extends Hashtable<String, Object>
{
/*
* Although this class is no more than a Hashtable
* , it is created for loose-coupled interaction with interfacing classes.
*/
private static final long serialVersionUID = 5524123527653934470L;
private BufferedReader reader = null;
private String inputString = null;
private void setReader(BufferedReader reader)
{
this.reader = reader;
}
private void setInputString(String inputString)
{
this.inputString = inputString;
}
protected BufferedReader getReader()
{
return reader;
}
protected String getInputString()
{
return inputString;
}
private ModuleContext()
{
super();
}
}
private ArrayList<Replaceable> listOfReplaceables;
protected Replaceable addReplaceable(Replaceable replaceable)
{
listOfReplaceables.add(replaceable);
return replaceable;
}
protected abstract void replace(ModuleContext moduleContext);
public void setPromptDisplayMethod(Displayable d)
{
protect();
this.displayMethod = d;
}
public ModuleContext display(TextParser parent)
{
if(this.isPromptDisplayEnabled())
return this.displayMethod.display(this, parent);
else
throw new TextParserException("INTERNAL: Attempt to invoke Display Method on a Module with no previous registration of a Display Method.");
}
protected String callMethod(Function<ModuleContext,String> method, ModuleContext moduleContext) throws Exception
{
String output = "";
output = method.apply(moduleContext);
return output;
}
public String runReplacements(Object input, ModuleContext moduleContext) throws Exception
{
protect();
if(moduleContext == null)
{
throw new TextParserException("ModuleContext cannot be NULL.");
}
listOfReplaceables = new ArrayList<Replaceable>();
if(input == null)
throw new TextParserException("An Unexpected Exception Occurred");
else if(input instanceof BufferedReader)
moduleContext.setReader((BufferedReader)input);
else if(input instanceof String)
moduleContext.setInputString((String)input);
replace(moduleContext);
Replaceable[] replaceables = listOfReplaceables.toArray(new Replaceable[0]);
for (int i=0 ; i<replaceables.length ; i++)
{
if(replaceables[i].replaceViaCallMethod())
{
moduleContext.setInputString(callMethod(replaceables[i].callMethodToReplace,moduleContext));
}
else
{
if(moduleContext.getInputString() != null)
moduleContext.setInputString(moduleContext.getInputString().replaceAll(replaceables[i].regex, replaceables[i].replacement));
else if(i==0)
{
throw new TextParserException("The first replaceable can not be created without a method call if File Read Support is enabled.");
}
}
}
return moduleContext.getInputString();
}
}
class Replaceable
{
protected String regex;
protected String replacement;
protected Function<Module.ModuleContext,String> callMethodToReplace = null;
public Replaceable(String regex, String replacement)
{
this.regex = regex;
this.replacement = replacement;
}
public Replaceable()
{
this.regex = "";
this.replacement = "";
}
public Replaceable setMethod(Function<Module.ModuleContext,String> method)
{
this.callMethodToReplace = method;
return this;
}
protected boolean replaceViaCallMethod()
{
return !(callMethodToReplace == null);
}
}
class TextParserException extends RuntimeException
{
/**
*
*/
private static final long serialVersionUID = 4310496290737841066L;
public TextParserException(String message)
{
super(message);
}
}