Skip to content

Commit 269d20f

Browse files
refactoring variable converter, adding access member op
1 parent c80d378 commit 269d20f

File tree

6 files changed

+105
-63
lines changed

6 files changed

+105
-63
lines changed

SerialX-core/src/main/java/org/ugp/serialx/GenericScope.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ public <V extends ValT> V get(KeyT variableKey)
267267
* @param variableKey | Variables name.
268268
* @param defaultValue | Default value to return.
269269
*
270-
* @return Value of variable with name or defaultValue if there is no such a one or given key contains null!
270+
* @return Value of variable with name or defaultValue if there is no such a one (or given key contains null)!
271271
*
272272
* @since 1.2.5
273273
*/
@@ -337,7 +337,7 @@ public <V extends ValT> V get(KeyT... pathToValue)
337337
* @param cls | Default value to return.
338338
* @param defaultValue | Class that you want the obtained object to be converted into! Exact conversion algorithm can differ based on its implementations.
339339
*
340-
* @return Value of variable with name given converted to object of cls or defaultValue if there is no such a one or given key contains null!
340+
* @return Value of variable with name given converted to object of cls or defaultValue if there is no such a one (or given key contains null)!
341341
*
342342
* @throws Exception | If converting to object of cls failed from some reason! This can differ from implementation to implementation! By default it uses {@link GenericScope#toObject(cls)}
343343
*

SerialX-core/src/main/java/org/ugp/serialx/Scope.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ else if (obj instanceof CharSequence)
514514
*/
515515
public String getString(int valueIndex)
516516
{
517-
return String.valueOf(get(valueIndex));
517+
return String.valueOf((Object) get(valueIndex));
518518
}
519519

520520
/**

SerialX-core/src/main/java/org/ugp/serialx/Utils.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public static Object InvokeFunc(Object obj, Class<?> objCls, String name, Class<
218218
*/
219219
public static <T> T Clone(T obj)
220220
{
221-
return Clone(obj, DataParser.REGISTRY, new Object[] {}, new Scope());
221+
return Clone(obj, DataParser.REGISTRY, new Object[0], new Scope());
222222
}
223223

224224
/**
@@ -458,8 +458,8 @@ public static StringBuilder multilpy(char ch, int times)
458458
*/
459459
public static StringBuilder multilpy(CharSequence str, int times)
460460
{
461-
StringBuilder sb = new StringBuilder(str);
462-
while (times-- > 1)
461+
StringBuilder sb = new StringBuilder();
462+
while (times-- > 0)
463463
sb.append(str);
464464
return sb;
465465
}
@@ -503,7 +503,7 @@ public static String[] splitValues(String s, int limit, boolean splitAfterSingle
503503
*
504504
* @since 1.3.5
505505
*/
506-
public static String[] splitValues(String s, int limit, boolean oneOrMore, char[] splitBreaks, char... splitter)
506+
public static String[] splitValues(String s, int limit, boolean oneOrMore, char[] splitBreaks, char... splitter) //TODO: This bs is terribly broken! Idk what I was doing back then but its not doing what I would assume at all...
507507
{
508508
if (splitter.length <= 0 || limit == 1)
509509
return new String[] {s};
@@ -727,9 +727,12 @@ public static boolean isOneOf(int ch, char... chars)
727727
public static boolean contains(CharSequence str, char... oneOf)
728728
{
729729
if (oneOf.length == 1)
730+
{
730731
for (int i = 0, len = str.length(); i < len; i++)
731732
if (str.charAt(i) == oneOf[0])
732733
return true;
734+
return false;
735+
}
733736

734737
for (int i = 0, len = str.length(); i < len; i++)
735738
if (isOneOf(str.charAt(i), oneOf))

SerialX-core/src/main/java/org/ugp/serialx/converters/ProtocolConverter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import static org.ugp.serialx.Utils.Instantiate;
44
import static org.ugp.serialx.Utils.indexOfNotInObj;
5-
import static org.ugp.serialx.Utils.isOneOf;
65
import static org.ugp.serialx.Utils.splitValues;
76

87
import java.io.Serializable;
@@ -115,7 +114,7 @@ protected Object parse(ParserRegistry myHomeRegistry, Class<?> objClass, String
115114

116115
String[] args = splitValues(str, ' ');
117116
int nameIndex;
118-
if (!isOneOf(args[0].charAt(0), '{', '[') && (nameIndex = args[0].indexOf("::")) > -1) //Is static member invocation
117+
if ((args[0].charAt(0) | ' ') != '{' && (nameIndex = args[0].indexOf("::")) > -1) //Is static member invocation
119118
{
120119
String memberName = args[0].substring(nameIndex + 2);
121120
if (!isAllowStaticMemberInvocation())

SerialX-core/src/main/java/org/ugp/serialx/converters/VariableParser.java

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
/**
1313
* This parser is capable of reading variables from {@link GenericScope} by using "$"!
14-
* {@link VariableConverter#parse(String, Object...)} required one additional Scope argument in args... at index 0!
14+
* {@link VariableConverter#parse(String, Object...)} required one additional Scope argument in args... at index 0!<br>
15+
* It also manages access member operator also known as separator <code>"."</code>.
1516
* Its case sensitive!<br>
1617
* Exact outputs of this converter are based on inserted scope!
1718
*
@@ -31,54 +32,63 @@ public Object parse(ParserRegistry myHomeRegistry, String str, Object... args)
3132
}
3233
return CONTINUE;
3334
}
35+
36+
/**
37+
* @param source | Source object to get value of the member from (may or may not be null). Source should not be modified!
38+
* @param member | Name/key of the member to get.
39+
*
40+
* @return The value of member from given source. You can think about this as ekvivalent to <code>source.member</code> in Java. If member with provided name/key is not present in the source or its value is not possible to get, {@link VOID} has to be returned! If source can't be accessed/dereferenced, <code>null</code> has to be returned!<br>
41+
* Note: This method is meant to be overridden in order to add support for accessing multiple sources because by default it supports only {@link GenericScope}
42+
*
43+
* @since 1.3.7
44+
*/
45+
@SuppressWarnings("unchecked")
46+
public Object getMemberOperator(Object source, Object member)
47+
{
48+
if (source instanceof GenericScope)
49+
return ((GenericScope<?, Object>) source).variables().getOrDefault(member, VOID);
50+
return null;
51+
}
3452

3553
/**
3654
* @param myHomeRegistry | Registry where this parser is registered provided by {@link DataParser#parseObj(Registry, String, boolean, Class[], Object...)} otherwise it demands on implementation (it should not be null)!
37-
* @param str | Source string (preferably with some variables to read)!
55+
* @param str | Source string, should not be null or empty (preferably with some variables to read)!
3856
* @param scope | Source scope to read from, can't be null!
3957
* @param args | Some additional args. This can be anything and it demands on implementation of DataParser.
4058
*
4159
* @return Value of variable read from scope is str was suitable. Special return types are {@link DataParser#VOID} and {@link DataParser#CONTINUE}. Continue will ignore this parser and jump to another one in registry.
4260
*
4361
* @since 1.3.7
4462
*/
45-
@SuppressWarnings("unchecked")
46-
protected Object parse(ParserRegistry myHomeRegistry, String str, GenericScope<?, Object> scope, Object... args)
63+
protected Object parse(ParserRegistry myHomeRegistry, String str, GenericScope<?, Object> scope, Object... args)
4764
{
48-
if (str.charAt(0) == '$' && !contains(str, ' ', '+', '-', '*', '/', '%', '>', '<', '=', '&', '|', '^', '?', '='))
65+
if (str.charAt(0) == '$' && !contains(str = str.substring(1), ' ', '+', '-', '*', '/', '%', '>', '<', '=', '&', '|', '^', '?', '='))
4966
{
50-
5167
boolean clsModif = str.endsWith("::class"), newModif = false; // Handle modifiers...
5268
if (clsModif)
5369
str = str.substring(0, str.length()-7);
5470
else if (newModif = str.endsWith("::new"))
5571
str = str.substring(0, str.length()-5);
5672

5773
Object obj = null;
58-
if ((str = str.substring(1)).indexOf('.') > -1)
74+
if (str.indexOf('.') > -1)
5975
{
60-
Object[] path = splitValues(str, '.');
76+
String[] path = splitValues(str, '.');
6177
int iLast = path.length-1;
6278

6379
backlook: do
6480
{
65-
Object sc = scope.variables().getOrDefault(path[0], VOID);
66-
if (sc instanceof GenericScope) // The first one has to be scope!
81+
Object sc;
82+
if ((sc = getMemberOperator(scope, path[0])) != VOID) // Attempt to get only when exists...
6783
{
68-
for (int i = 1; i < iLast; i++) // Subscope/forward lookup...
69-
if (!((sc = ((GenericScope<Object, ?>) sc).get(path[i])) instanceof GenericScope))
84+
for (int i = 1; i < iLast; i++) // Subscope/forward lookup (inner path only)...
85+
if ((sc = getMemberOperator(sc, path[i])) == null || sc == VOID)
7086
{
7187
// LogProvider.instance.logErr("Value of path \"" + arg + "\" cannot be dereferenced because \"" + path[i] + "\" is not a scope but " + sc + "!", null);
7288
break backlook;
7389
}
7490

75-
obj = ((GenericScope<?, Object>) sc).variables().get(path[iLast]);
76-
break;
77-
}
78-
79-
if (sc != VOID) // = variable was defined in parent but it is not a scope, it means we want to break cos we can't deref that = treat the path as invalid (undefined)...
80-
{
81-
// LogProvider.instance.logErr("Value of path \"" + arg + "\" cannot be dereferenced because \"" + path[0] + "\" is not a scope but " + sc + "!", null);
91+
obj = getMemberOperator(sc, path[iLast]);
8292
break;
8393
}
8494
}
@@ -96,8 +106,9 @@ else if (newModif = str.endsWith("::new"))
96106

97107
if (obj == null || obj == VOID) // When was not found...
98108
return null;
99-
return clsModif ? obj.getClass() : newModif ? Clone(obj, scope instanceof Serializer ? ((Serializer) scope).getParsers() : DataParser.REGISTRY, new Object[] {}, new Scope()) : obj;
109+
return clsModif ? obj.getClass() : newModif ? Clone(obj, scope instanceof Serializer ? ((Serializer) scope).getParsers() : DataParser.REGISTRY, new Object[0], new Scope()) : obj;
100110
}
111+
101112
return CONTINUE;
102113
}
103114
}

SerialX-juss/src/main/java/org/ugp/serialx/juss/converters/VariableConverter.java

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package org.ugp.serialx.juss.converters;
22

33
import static org.ugp.serialx.Utils.contains;
4-
import static org.ugp.serialx.Utils.fastReplace;
54
import static org.ugp.serialx.Utils.multilpy;
65
import static org.ugp.serialx.Utils.splitValues;
76

87
import java.util.AbstractMap;
9-
import java.util.Arrays;
108
import java.util.Map;
119
import java.util.Map.Entry;
1210

@@ -17,13 +15,16 @@
1715

1816
/**
1917
* This converter is capable of converting {@link Map.Entry} and reading variables from {@link GenericScope} by using "$"!
20-
* {@link VariableConverter#parse(String, Object...)} required one additional Scope argument in args... at index 0!
18+
* {@link VariableConverter#parse(String, Object...)} required one additional Scope argument in args... at index 0!<br>
19+
* It manages assign operator <code>=</code> as well as access member operator also known as separator <code>"."</code>.<br>
2120
* Its case sensitive!<br>
2221
* Exact outputs of this converter are based on inserted scope!
2322
*
2423
* @author PETO
2524
*
2625
* @since 1.3.0
26+
*
27+
* @see VariableParser
2728
*/
2829
public class VariableConverter extends VariableParser implements DataConverter
2930
{
@@ -57,64 +58,68 @@ public Object parse(ParserRegistry myHomeRegistry, String arg, Object... args)
5758
{
5859
if (args.length > 0 && arg.length() > 0 && args[0] instanceof GenericScope)
5960
{
60-
GenericScope<Object, Object> scope = (GenericScope<Object, Object>) args[0];
61-
boolean genericVar = args.length > 4 && args[4] == GenericScope.class;
61+
GenericScope<?, Object> scope = (GenericScope<?, Object>) args[0];
6262
if (isVarAssignment(arg))
6363
{
64-
String[] enrty = splitValues(arg, 0, false, new char[] {'?'}, '=', ':');
65-
66-
Object obj = null;
67-
String objString = enrty[enrty.length-1];
64+
boolean getValueModif = arg.charAt(0) == '$', genericVar = args.length > 4 && args[4] == GenericScope.class;
65+
if (getValueModif)
66+
arg = arg.substring(1);
67+
68+
String vars[] = splitValues(arg, 0, false, new char[] {'?'}, '=', ':'), valStr;
6869

69-
if (enrty.length > 1 && !objString.isEmpty())
70+
Object val = null;
71+
int iVal = vars.length-1;
72+
if (vars.length > 1 && !(valStr = vars[iVal]).isEmpty())
7073
{
71-
obj = myHomeRegistry.parse(objString, args);
74+
val = myHomeRegistry.parse(valStr, args);
7275
}
7376

74-
for (int i = 0; i < enrty.length-1; i++)
77+
eachVar: for (int i = 0; i < iVal; i++) // Support for assigning multiple vars to the same value...
7578
{
76-
if (!genericVar && contains(enrty[i] = enrty[i].trim(), ' '))
77-
LogProvider.instance.logErr("Variable name \"" + enrty[i] + "\" is invalid, blank characters are not allowed!", null);
78-
else
79+
String var = vars[i];
80+
if (!genericVar && contains(var, ' '))
81+
LogProvider.instance.logErr("Variable name \"" + var + "\" is invalid, blank characters are not allowed!", null);
82+
else if (var.indexOf('.') > -1)
7983
{
80-
if ((enrty[i] = fastReplace(enrty[i], "$", "")).indexOf('.') > -1)
84+
String[] path = splitValues(var, '.');
85+
int iLast = path.length-1, j = 0;
86+
87+
backlook: do
8188
{
82-
String[] tree = splitValues(enrty[i], '.');
83-
GenericScope<Object, Object> sc = (GenericScope<Object, Object>) scope.getGenericScope((Object[]) Arrays.copyOfRange(tree, 0, tree.length-1));
84-
if (sc != null)
89+
Object sc;
90+
if ((sc = getMemberOperator(scope, path[0])) != VOID) // Attempt to get only when exists...
8591
{
86-
if (obj == VOID)
87-
sc.variables().remove(enrty[i]);
88-
else
89-
sc.put(genericVar ? myHomeRegistry.parse(tree[tree.length-1], true, null, args) : tree[tree.length-1], obj);
92+
for (j = 1; j < iLast; j++) // Subscope/forward lookup (inner path only)...
93+
if ((sc = getMemberOperator(sc, path[j])) == null || sc == VOID)
94+
break backlook;
95+
96+
setMemberOperator(myHomeRegistry, sc, path[iLast], val, genericVar, args);
97+
continue eachVar;
9098
}
91-
else
92-
LogProvider.instance.logErr("Variable \"" + tree[tree.length-2] + "\" was not declared as scope in its scope so variable \"" + tree[tree.length-1] +"\" cant be set to \"" + obj + "\"!", null);
9399
}
94-
else if (obj == VOID)
95-
scope.variables().remove(enrty[i]);
96-
else
97-
scope.put(genericVar ? myHomeRegistry.parse(enrty[i], true, null, args) : enrty[i], obj);
100+
while ((scope = scope.getParent()) != null);
101+
102+
LogProvider.instance.logErr("Path \"" + var + "\" cannot be set to \"" + val + "\" because \"" + path[j] + "\" is not a accessible or does not exist!", null);
98103
}
104+
else
105+
setMemberOperator(myHomeRegistry, scope, var, val, genericVar, args);
99106
}
100107

101-
if (arg.charAt(0) == '$')
102-
return obj;
103-
return VOID;
108+
return getValueModif ? val : VOID;
104109
}
105110

106111
return parse(myHomeRegistry, arg, scope, args); //Reading vars from scope...
107112
}
113+
108114
return CONTINUE;
109115
}
110116

111-
@SuppressWarnings("unchecked")
112117
@Override
113118
public CharSequence toString(ParserRegistry myHomeRegistry, Object obj, Object... args)
114119
{
115120
if (obj instanceof Entry)
116121
{
117-
Entry<Object, Object> var = (Entry<Object, Object>) obj;
122+
Entry<?, ?> var = (Entry<?, ?>) obj;
118123
int tabs = 0;
119124
if (args.length > 1 && args[1] instanceof Integer)
120125
tabs = (int) args[1];
@@ -136,6 +141,30 @@ public CharSequence getDescription(ParserRegistry myHomeRegistry, Object objToDe
136141
return new StringBuilder(myHomeRegistry.getConverterFor(ent.getValue(), argsUsedConvert).getDescription(myHomeRegistry, ent.getValue(), argsUsedConvert)).append(" Stored by \"").append(ent.getKey()).append("\" variable!");
137142
}
138143

144+
/**
145+
* @param myHomeRegistry | {@link ParserRegistry} provided by caller, may or may not be used...
146+
* @param source | Source object to set the value member.
147+
* @param member | Name/key of the member to set.
148+
* @param val | Value to set the member to.
149+
* @param genericVar | If true, member is expected be generic (not only string) and further parsing is required, may or may not be used...
150+
* @param args | Some additional args to be used in case of parsing that are provided by called, may or may not be used...
151+
*
152+
* @return By default it returns the previous value of the member. If member with provided name/key is not present in the source or its value is not possible to set, {@link VOID} should be returned!
153+
*
154+
* @since 1.3.7
155+
*/
156+
@SuppressWarnings("unchecked")
157+
public Object setMemberOperator(ParserRegistry myHomeRegistry, Object source, String member, Object val, boolean genericVar, Object... args)
158+
{
159+
if (source instanceof GenericScope)
160+
{
161+
if (val == VOID)
162+
return ((GenericScope<Object,?>) source).remove(member);
163+
return ((GenericScope<Object, Object>) source).put(genericVar ? myHomeRegistry.parse(member, true, null, args) : member, val);
164+
}
165+
return VOID;
166+
}
167+
139168
/**
140169
* @return True if variables will be serialized using json style ("key" : value)!
141170
*

0 commit comments

Comments
 (0)