source: java/main/src/main/java/com/framsticks/params/ParamBuilder.java @ 100

Last change on this file since 100 was 100, checked in by psniegowski, 11 years ago

HIGHLIGHTS:

  • add <include/> to configuration
  • add side notes to tree
    • used to store arbitrary information alongside the tree structure
  • migrate to log4j2
    • supports lazy string evaluation of passed arguments
  • improve GUI tree
    • it stays in synchronization with actual state (even in high load test scenario)
  • improve panel management in GUI
  • make loading objects in GUI more lazy
  • offload parsing to connection receiver thread
    • info parsing
    • first step of objects parsing
  • fix connection parsing bug (eof in long values)
  • support zero-arguments procedure in table view

CHANGELOG:
Implement procedure calls from table view.

Refactorization around procedures in tables.

Add table editor for buttons.

Render buttons in the the list view.

Further improve Columns.

Add Column class for TableModel?.

Accept also non-arguments ProcedureParams? in tableView.

Increase maximal TextAreaControl? size.

Add tooltip to ProcedureControl?.

Fix bug of interpreting eofs in long values by connection reader.

Further rework connection parsing.

Simplify client connection processing.

Test ListChange? modification.

Test ListChange? events with java server.

Add TestChild?.

Fix bug with fast deregistering when connecting to running server.

Another minor refactorization in TreeOperations?.

Fix bug in SimpleAbstractAccess? loading routine.

Another minor improvement.

Minor change.

Make reading of List objects two-phase.

Another minor change.

Dispatch parsing into receiver thread.

Another step.

Enclose passing value in ObjectParam? case in closure.

Minor step.

Minor change on way to offload parsing.

Temporarily comment out single ValueParam? get.

It will be generalized to multi ValueParam?.

Process info in receiver thread.

Add DispatchingExceptionHandler?.

Make waits in browser test longer.

Use FETCHED_MARK.

It is honored in GUI, where it used to decide whether to get values

after user action.

It is set in standard algorithm for processing fetched values.

Add remove operation to side notes.

Make loading more lazy.

Improve loading policy.

On node choose load itself, on node expansion, load children.

Minor improvement.

Fix bug with panel interleaving.

Minor improvements.

Improve panel management.

More cleaning around panels.

Reorganize panels.

Further improve tree.

Fix bug in TreeModel?.

Remove children from TreeNode?.

Implement TreeNode? hashCode and equals.

Make TreeNode? delegate equals and hashcode to internal reference.

Move listeners from TreeNode? to side notes.

Store path.textual as a side note.

Side note params instead of accesses for objects.

More refactorizations.

In TreeNode? bindAccess based on side notes.

Minor step.

Hide createAccess.

Rename AccessInterface? to Access.

Minor changes.

Several improvements in high load scenarios.

Change semantics of ArrayListAccess?.set(index, null);

It now removes the element, making list shorter
(it was set to null before).

Add path remove handler.

Handle exceptions in Connection.

Update .gitignore

Configure logging to file.

Move registration to TreeModel?.

Further refactorization.

Minor refactorization.

Minor improvements.

Use specialized event also for Modify action of ListChange?.

Use remove events.

Use the insertion events for tree.

Further improve tree refreshing.

Further improve reacting on events in GUI.

Fix problem with not adding objects on addition list change.

Migrate to log4j lazy String construction interface.

Migrate imports to log4j2.

Drop dependency on adapter to version 1.2.

Switch log4j implementation to log4j2.

Add dirty mark to the NodeAtFrame?.

Make selecting in AccessInterfaces? type safe.

Ignore containers size settings in Model and Genotype.

Use tree side notes to remember local changes and panels.

Add sideNotes to tree.

They will be used to store various accompanying information
right in the tree.

Use ReferenceIdentityMap? from apache in TreeNode?.

It suits the need perfectly (weak semantics on both key and value).

Make ArrayListParam? do not react size changes.

Guard in TableModel? before not yet loaded objects.

Add <include/> clause and AutoInjector?.

Extract common columns configuration to separate xml,
that can be included by other configurations.

File size: 12.0 KB
Line 
1package com.framsticks.params;
2
3import com.framsticks.params.annotations.FramsClassAnnotation;
4import com.framsticks.params.annotations.ParamAnnotation;
5import com.framsticks.params.types.*;
6import com.framsticks.util.Builder;
7import com.framsticks.util.FramsticksException;
8import com.framsticks.util.Misc;
9import com.framsticks.util.lang.FlagsUtil;
10import com.framsticks.util.lang.Strings;
11
12import org.apache.logging.log4j.Logger;
13import org.apache.logging.log4j.LogManager;
14
15import java.lang.reflect.InvocationTargetException;
16import java.util.ArrayList;
17import java.util.Arrays;
18import java.util.List;
19import java.util.regex.Matcher;
20import java.util.regex.Pattern;
21
22import javax.annotation.Nonnull;
23
24/**
25 * The class ParamBuilder helps building Param objects.
26 *
27 * @author Mateusz Jarus <name.surname@gmail.com> (please replace name and
28 *         surname with my personal data)
29 *
30 * @author Piotr Śniegowski
31 */
32
33@FramsClassAnnotation(name = "prop", id = "prop")
34public class ParamBuilder implements Builder<Param> {
35        private final static Logger log = LogManager.getLogger(ParamBuilder.class.getName());
36
37        private static final String ID_FIELD = "id";
38        private static final String NAME_FIELD = "name";
39        private static final String HELP_FIELD = "help";
40        private static final String GROUP_FIELD = "group";
41        private static final String TYPE_FIELD = "type";
42        private static final String FLAGS_FIELD = "flags";
43
44        /** The parameter id. */
45        private String id;
46
47        /** The number of group, that parameter belongs to. */
48        private Integer group;
49
50        /** The flags stored as a bit sum. */
51        private int flags = 0;
52
53        /** The parameter name. */
54        private String name;
55
56        /** The help (description) concerning parameter. */
57        private String help;
58
59        /** The type of parameter. */
60        private Class<? extends Param> paramType;
61
62        private Object min;
63
64        private Object max;
65
66        private Object def;
67
68        private int extra = 0;
69
70        protected String containedTypeName;
71
72        protected String eventArgumentTypeName;
73
74        protected Class<?> storageType;
75
76        protected FramsClassBuilder classBuilder;
77
78        public ParamBuilder() {
79                this(null);
80        }
81
82        protected ValueParam resultType;
83
84        protected List<ValueParam> argumentsType;
85
86        /**
87         * @param classBuilder
88         */
89        public ParamBuilder(FramsClassBuilder classBuilder) {
90                this.classBuilder = classBuilder;
91        }
92
93
94        /**
95         * @return the min
96         */
97        @ParamAnnotation
98        public Object getMin() {
99                return min;
100        }
101
102        /**
103         * @return the max
104         */
105        @ParamAnnotation
106        public Object getMax() {
107                return max;
108        }
109
110        /**
111         * @return the def
112         */
113        @ParamAnnotation
114        public Object getDef() {
115                return def;
116        }
117
118        public String getContainedTypeName() {
119                return Strings.notEmpty(containedTypeName) ? containedTypeName : null;
120        }
121
122        public ParamBuilder containedTypeName(String containedTypeName) {
123                this.containedTypeName = containedTypeName;
124                return this;
125        }
126
127        /**
128         * @return the resultType
129         */
130        public ValueParam getResultType() {
131                return resultType;
132        }
133
134
135        /**
136         * @param resultType the resultType to set
137         */
138        public ParamBuilder resultType(ValueParam resultType) {
139                this.resultType = resultType;
140                return this;
141        }
142
143        /**
144         * @return the argumentsType
145         */
146        public List<ValueParam> getArgumentsType() {
147                return argumentsType;
148        }
149
150
151        /**
152         * @param argumentsType the argumentsType to set
153         */
154        public ParamBuilder argumentsType(List<ValueParam> argumentsType) {
155                this.argumentsType = argumentsType;
156                return this;
157        }
158
159        /**
160         * @return the enumValues
161         */
162        public List<String> getEnumValues() {
163                return enumValues;
164        }
165
166        /**
167         * @return the uid
168         */
169        public String getUid() {
170                return uid;
171        }
172
173        public ParamBuilder uid(String uid) {
174                this.uid = uid;
175                return this;
176        }
177
178        public @Nonnull <T extends Param> T finish(Class<T> requested) {
179                Param param = finish();
180                if (!requested.isInstance(param)) {
181                        throw new FramsticksException().msg("param is of wrong type").arg("requested", requested).arg("actual", param.getClass());
182                }
183                return requested.cast(param);
184        }
185
186        /**
187         * Build Param based on provided data.
188         *
189         * @return Param object
190         * @throws Exception
191         *             when Param getType is not defined
192         */
193        public @Nonnull Param finish() {
194                try {
195                        if (paramType == null) {
196                                throw new FramsticksException().msg("trying to finish incomplete param while type is missing");
197                        }
198                        return paramType.getConstructor(ParamBuilder.class).newInstance(this);
199                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | FramsticksException e) {
200                        throw new FramsticksException().msg("failed to create param").cause(e).arg("name", name);
201                }
202        }
203
204        @ParamAnnotation
205        public ParamBuilder id(String id) {
206                this.id = id;
207                return this;
208        }
209
210        public <T extends Param> ParamBuilder type(Class<T> type) {
211                assert type != null;
212                this.paramType = type;
213                return this;
214        }
215
216        /**
217         * @return the id
218         */
219        @ParamAnnotation
220        public String getId() {
221                return id;
222        }
223
224        @ParamAnnotation
225        public ParamBuilder group(Integer group) {
226                this.group = group;
227                return this;
228        }
229
230        @ParamAnnotation
231        public ParamBuilder flags(int flags) {
232                this.flags = flags;
233                return this;
234        }
235
236        @ParamAnnotation
237        public ParamBuilder name(String name) {
238                this.name = name;
239                return this;
240        }
241
242        protected <T extends Number> void parseMinMaxDefNumber(Class<T> type, String second, String third, String fourth) {
243                if (second != null) {
244                        min = second;
245                }
246                if (third != null) {
247                        max = third;
248                }
249                if (fourth != null) {
250                        def = fourth;
251                }
252        }
253
254        protected List<String> enumValues;
255
256        public ParamBuilder enums(List<String> values) {
257                enumValues = values;
258                return type(EnumParam.class);
259        }
260
261        protected String uid;
262
263        @ParamAnnotation
264        public ParamBuilder type(String type) {
265                // typeString = type;
266                assert type != null;
267
268                log.trace("parsing type: {}", type);
269
270                String[] typeSplitted = type.split(" ");
271                String first = typeSplitted[0];
272                String second = typeSplitted.length > 1 ? typeSplitted[1] : null;
273                String third = typeSplitted.length > 2 ? typeSplitted[2] : null;
274                String fourth = typeSplitted.length > 3 ? typeSplitted[3] : null;
275
276                switch (first.charAt(0)) {
277                        case 'o': {
278                                containedTypeName = second != null ? second : first.substring(1);
279                                type(ObjectParam.class);
280                                break;
281                        }
282                        case 'p': {
283                                type(ProcedureParam.class);
284                                signature(type.substring(1));
285                                break;
286                        }
287                        case 'd': {
288
289                                int tildeIndex = type.indexOf("~");
290                                if (tildeIndex != -1) {
291                                        enums(Arrays.asList(type.substring(tildeIndex + 1).split("~")));
292                                } else {
293                                        if (first.length() >= 2) {
294                                                switch (first.charAt(1)) {
295                                                        case 'b': {
296                                                                type(BinaryParam.class);
297                                                                break;
298                                                        }
299                                                        case 'c': {
300                                                                type(ColorParam.class);
301                                                                break;
302                                                        }
303                                                        default: {
304                                                                log.error("unknown type: {}", first);
305                                                                return this;
306                                                        }
307                                                }
308                                        }
309                                        if ("0".equals(second) && "1".equals(third)) {
310                                                type(BooleanParam.class);
311                                        }
312                                        if (paramType == null) {
313                                                type(DecimalParam.class);
314                                        }
315                                }
316                                if (DecimalParam.class.isAssignableFrom(this.paramType)) {
317                                        parseMinMaxDefNumber(Integer.class, second, third, fourth);
318                                }
319                                break;
320                        }
321                        case 'f': {
322                                type(FloatParam.class);
323                                parseMinMaxDefNumber(Double.class, second, third, fourth);
324                                break;
325                        }
326                        case 'x': {
327                                type(UniversalParam.class);
328                                break;
329                        }
330                        case 's': {
331                                type(StringParam.class);
332                                min(second);
333                                max(third);
334                                break;
335                        }
336                        case 'e': {
337                                type(EventParam.class);
338                        eventArgumentTypeName(second);
339                        break;
340                }
341                case 'l': {
342                        containedTypeName = second;
343                        if (third != null) {
344                                type(UniqueListParam.class);
345                                uid = third;
346                        } else {
347                                type(ArrayListParam.class);
348                        }
349                        break;
350                }
351                default: {
352                        log.error("unknown type: {}", first);
353                        return this;
354                }
355                }
356                return this;
357        }
358
359        public ParamBuilder eventArgumentTypeName(String eventArgumentTypeName) {
360                this.eventArgumentTypeName = eventArgumentTypeName;
361                return this;
362        }
363
364        @ParamAnnotation
365        public ParamBuilder help(String help) {
366                this.help = help;
367                return this;
368        }
369
370        /**
371         * @return the group
372         */
373        @ParamAnnotation
374        public Integer getGroup() {
375                return group;
376        }
377
378        /**
379         * @return the flags
380         */
381        @ParamAnnotation
382        public int getFlags() {
383                return flags;
384        }
385
386        /**
387         * @return the name
388         */
389        @ParamAnnotation
390        public String getName() {
391                return name;
392        }
393
394        /**
395         * @return the help
396         */
397        @ParamAnnotation
398        public String getHelp() {
399                return help;
400        }
401
402        @ParamAnnotation
403        public String getType() {
404                return "?";
405        }
406
407        @ParamAnnotation(id = "xtra")
408        public int getExtra() {
409                return extra;
410        }
411
412        /**
413         * @return the paramType
414         */
415        public Class<? extends Param> getParamType() {
416                return paramType;
417        }
418
419        @ParamAnnotation(id = "xtra")
420        public ParamBuilder extra(int extra) {
421                this.extra = extra;
422                return this;
423        }
424
425        @ParamAnnotation
426        public ParamBuilder min(Object min) {
427                this.min = min;
428                return this;
429        }
430
431        @ParamAnnotation
432        public ParamBuilder max(Object max) {
433                this.max = max;
434                return this;
435        }
436
437        @ParamAnnotation
438        public ParamBuilder def(Object def) {
439                this.def = def;
440                return this;
441        }
442
443
444        public Param build(String line) throws Exception {
445                String[] paramEntryValues = line.split(",");
446
447                if (paramEntryValues.length == 0) {
448                        log.warn("field empty or wrong format ({}) - omitting", line);
449                        return null;
450                }
451
452                for (int i = 0; i < paramEntryValues.length; ++i) {
453                        paramEntryValues[i] = paramEntryValues[i].trim();
454                }
455
456                try {
457                        id(paramEntryValues[0]);
458                        group(Integer.valueOf(paramEntryValues[1]));
459                        flags(FlagsUtil.read(ParamFlags.class, paramEntryValues[2]));
460                        name(paramEntryValues[3]);
461                        type(paramEntryValues[4]);
462                        help(paramEntryValues[6]);
463                } catch (IndexOutOfBoundsException e) {
464                        /** everything is ok, parameters have just finished*/
465                } catch (NumberFormatException ex) {
466                        log.warn("wrong format of entry: {}, omitting", line);
467                        return null;
468                }
469                return finish();
470        }
471
472        public void setField(String key, String value) {
473                switch (key) {
474                        case ID_FIELD:
475                                id(value);
476                                break;
477                        case NAME_FIELD:
478                                name(value);
479                                break;
480                        case TYPE_FIELD:
481                                type(value);
482                                break;
483                        case FLAGS_FIELD:
484                                flags(FlagsUtil.read(ParamFlags.class, value));
485                                break;
486                        case HELP_FIELD:
487                                help(value);
488                                break;
489                        case GROUP_FIELD:
490                                group(Integer.valueOf(value));
491                                break;
492                        default:
493                                log.error("unknown field for Param: {}", key);
494                                break;
495                }
496        }
497
498        public ParamBuilder fillDef(Object def) {
499                if (this.def == null) {
500                        return def(def);
501                }
502                return this;
503        }
504
505        public ParamBuilder fillStorageType(Class<?> storageType) {
506                if (this.storageType == null) {
507                        this.storageType = storageType;
508                }
509                return this;
510        }
511
512        /**
513         * @return the eventArgumentTypeName
514         */
515        public String getEventArgumentTypeName() {
516                return eventArgumentTypeName;
517        }
518
519        public Class<?> getStorageType() {
520                return storageType;
521        }
522
523        protected static ValueParam parseProcedureTypePart(String type, String name) {
524                return Param.build().type(type).name(name).id(name).finish(ValueParam.class);
525        }
526
527        private static Pattern signaturePattern = Pattern.compile("^([^\\(]+)?\\(([^\\)]*)\\)$");
528
529        public ParamBuilder signature(String signature) {
530                argumentsType = new ArrayList<>();
531
532                if (!Strings.notEmpty(signature)) {
533                        resultType = null;
534                        return this;
535                }
536                Matcher matcher = signaturePattern.matcher(signature);
537                if (!matcher.matches()) {
538                        throw new FramsticksException().msg("invalid signature");
539                }
540                String result = Strings.collapse(matcher.group(1));
541                if (result != null) {
542                        resultType = Param.build().type(result).finish(ValueParam.class);
543                } else {
544                        resultType = null;
545                }
546                String arguments = matcher.group(2);
547                if (!Strings.notEmpty(arguments)) {
548                        return this;
549                }
550                int number = 0;
551                for (String a : arguments.split(",")) {
552                        ParamBuilder arg = Param.build();
553
554                        int space = a.indexOf(' ');
555                        if (space == -1) {
556                                arg.type(a).id("arg" + number);
557                        } else {
558                                String name = a.substring(space + 1);
559                                arg.type(a.substring(0, space)).id(name).name(name);
560                        }
561                        argumentsType.add(arg.finish(ValueParam.class));
562                        ++number;
563                }
564                return this;
565        }
566
567
568        public ParamBuilder idAndName(String name) {
569                id(name);
570                name(name);
571                return this;
572        }
573
574        @Override
575        public String toString() {
576                return "ParamBuilder for " + Misc.returnNotNull(id, "<not yet known>");
577        }
578}
579
Note: See TracBrowser for help on using the repository browser.