source: java/main/src/main/java/com/framsticks/params/AccessOperations.java @ 193

Last change on this file since 193 was 193, checked in by Maciej Komosinski, 10 years ago

Set svn:eol-style native for all textual files

  • Property svn:eol-style set to native
File size: 14.9 KB
Line 
1package com.framsticks.params;
2
3import java.util.LinkedList;
4import java.util.ListIterator;
5
6import javax.annotation.Nullable;
7
8import org.apache.commons.lang3.ClassUtils;
9import org.apache.logging.log4j.Logger;
10import org.apache.logging.log4j.LogManager;
11
12import com.framsticks.communication.File;
13import com.framsticks.params.types.ListParam;
14import com.framsticks.parsers.MultiParamLoader;
15import com.framsticks.structure.messages.Result;
16import com.framsticks.util.FramsticksException;
17import com.framsticks.util.FramsticksUnsupportedOperationException;
18import com.framsticks.util.Misc;
19import com.framsticks.util.UnimplementedException;
20import com.framsticks.util.lang.Casting;
21import com.framsticks.util.lang.Containers;
22import com.framsticks.util.lang.Holder;
23import com.framsticks.util.lang.Pair;
24// import com.framsticks.util.lang.Containers;
25
26import static com.framsticks.params.SetStateFlags.*;
27import static com.framsticks.util.lang.Containers.filterInstanceof;
28
29public final class AccessOperations {
30
31        private final static Logger log = LogManager.getLogger(AccessOperations.class);
32
33        /**
34         *
35         */
36        private AccessOperations() {
37        }
38
39        /**
40         * Simple String key, value class.
41         */
42        public static class Entry {
43
44                public final String key;
45                public final String value;
46
47                public Entry(String key, String value) {
48                        this.key = key;
49                        this.value = value;
50                }
51
52                @Override
53                public String toString() {
54                        return key + " = " + value;
55                }
56        }
57
58        private static Entry readEntry(Source source) {
59
60                String line;
61                String key = null;
62                StringBuilder value = null;
63                while ((line = source.readLine()) != null) {
64                        if (key == null) {
65                                int colonIndex = line.indexOf(':');
66                                if (colonIndex == -1) {
67                                        return null;
68                                }
69                                key = line.substring(0, colonIndex);
70                                String inlineValue = line.substring(colonIndex + 1);
71
72
73                                if (!inlineValue.startsWith("~")) {
74                                        return new Entry(key, inlineValue);
75                                }
76                                value = new StringBuilder();
77                                value.append(inlineValue.substring(1));
78                                continue;
79                        }
80                        if (value.length() != 0) {
81                                value.append(System.getProperty("line.separator"));
82                        }
83                        if (line.endsWith("~") && !line.endsWith("\\~")) {
84                                value.append(line.substring(0, line.length() - 1));
85                                return new Entry(key, value.toString().replaceAll("\\\\~", "~"));
86                        }
87                        value.append(line);
88                }
89                return null;
90        }
91
92        public static <A extends Access> A assureSelected(A access) {
93                if (access.getSelected() == null) {
94                        access.select(access.createAccessee());
95                }
96                return access;
97        }
98
99        public static Access loadAll(@Nullable final Access rootAccess, Source source, final Registry registry) {
100                final MultiParamLoader loader = new MultiParamLoader();
101                log.trace("loading all from {} into {}", source, rootAccess);
102                loader.setNewSource(source);
103                final LinkedList<Access> accessesStack = new LinkedList<>();
104                if (rootAccess != null) {
105                        assureSelected(rootAccess);
106                        accessesStack.add(rootAccess);
107                }
108                final Holder<Boolean> first = new Holder<>(true);
109                final Holder<Boolean> needAdd = new Holder<>();
110                final Holder<Access> currentAccess = new Holder<>();
111                final Holder<Pair<Access, CompositeParam>> parent = new Holder<>();
112
113                loader.setAccessProvider(new AccessProvider() {
114                        @Override
115                        public Access getAccess(String name) {
116                                if (first.get()) {
117                                        first.set(false);
118                                        if (rootAccess != null) {
119                                                if (name.equals(rootAccess.getTypeId())) {
120                                                        needAdd.set(false);
121                                                        currentAccess.set(rootAccess);
122                                                        return rootAccess;
123                                                }
124                                        } else {
125                                                Access access = registry.createAccess(name);
126                                                needAdd.set(false);
127                                                currentAccess.set(access);
128                                                return access;
129
130                                        }
131                                }
132
133                                ListIterator<Access> accessIterator = accessesStack.listIterator(accessesStack.size());
134                                parent.set(null);
135                                // log.debug("accesses stack: {}", accessesStack);
136
137                                while (accessIterator.hasPrevious()) {
138                                        Access a = accessIterator.previous();
139                                        assert a != null;
140
141                                        for (CompositeParam p : Containers.filterInstanceof(a.getParams(), CompositeParam.class)) {
142                                                if (p.getContainedTypeName().equals(name)) {
143
144                                                        if (parent.get() != null) {
145                                                                throw new FramsticksException().msg("ambiguity encountered during loading").arg("name", name);
146                                                        }
147
148                                                        if (p instanceof ListParam) {
149                                                                ListAccess listAccess = Casting.assertCast(ListAccess.class, registry.prepareAccess(p, true));
150                                                                Object list = a.get(p, Object.class);
151                                                                if (list == null) {
152                                                                        list = listAccess.createAccessee();
153                                                                        a.set(p, list);
154                                                                }
155                                                                listAccess.select(list);
156                                                                parent.set(new Pair<Access, CompositeParam>(listAccess, listAccess.prepareParamFor(Integer.toString(listAccess.getParamCount()))));
157
158                                                        } else {
159                                                                parent.set(Pair.make(a, p));
160                                                        }
161                                                }
162                                        }
163
164                                        if (parent.get() == null) {
165                                                log.trace("{} cannot be placed in {}", name, a);
166                                                accessIterator.remove();
167                                        }
168                                }
169
170                                if (parent.get() == null) {
171                                        throw new FramsticksException().msg("failed to find place for loaded object").arg("name", name); //.arg("in", accessesStack);
172                                }
173
174                                currentAccess.set(registry.prepareAccess(parent.get().second, true));
175                                Object object = parent.get().first.get(parent.get().second, Object.class);
176                                if (object != null) {
177                                        currentAccess.get().select(object);
178                                        needAdd.set(false);
179                                } else {
180                                        object = currentAccess.get().createAccessee();
181                                        currentAccess.get().select(object);
182                                        needAdd.set(true);
183                                }
184
185                                return currentAccess.get();
186                        }
187                });
188
189                loader.addListener(MultiParamLoader.Status.AfterObject, new MultiParamLoader.StatusListener() {
190                        @Override
191                        public void onStatusChange() {
192                                if (needAdd.get()) {
193                                        parent.get().first.set(parent.get().second, currentAccess.get().getSelected());
194                                }
195                                if (currentAccess.get() != rootAccess)  {
196                                        accessesStack.add(currentAccess.get());
197                                }
198                                currentAccess.set(null);
199                        }
200                });
201
202                loader.go();
203                if (accessesStack.isEmpty()) {
204                        throw new FramsticksException().msg("failed to load from source").arg("source", source);
205                }
206                return accessesStack.get(0);
207        }
208
209        public static <S extends Sink> S saveAll(Access access, S sink, Registry registry) {
210                if (access instanceof ObjectAccess) {
211                        savePrimitives(access, sink);
212                }
213                for (CompositeParam p : filterInstanceof(access.getParams(), CompositeParam.class)) {
214                        Object child = access.get(p, Object.class);
215                        if (child == null) {
216                                continue;
217                        }
218                        saveAll(registry.prepareAccess(p, true).select(child), sink, registry);
219                }
220                return sink;
221        }
222
223        public static void saveComposites(Access access, Sink sink, Registry registry) {
224                for (CompositeParam p : filterInstanceof(access.getParams(), CompositeParam.class)) {
225                        Object child = access.get(p, Object.class);
226                        if (child == null) {
227                                continue;
228                        }
229                        savePrimitives(registry.prepareAccess(p, true).select(child), sink);
230                }
231        }
232
233        public static void savePrimitives(Access access, Sink sink) {
234                if (access instanceof ObjectAccess) {
235                        ObjectAccess objectAccess = (ObjectAccess) access;
236                        boolean headerNeeded = true;
237                        // sink.print(framsClass.getId()).print(":").breakLine();
238                        for (PrimitiveParam<?> p : filterInstanceof(access.getParams(), PrimitiveParam.class)) {
239
240                                Object value = objectAccess.get(p, Object.class);
241                                if ((value == null) || value.equals(p.getDef(Object.class))) {
242                                        continue;
243                                }
244
245                                if (headerNeeded) {
246                                        sink.print(access.getTypeId()).print(":").breakLine();
247                                        headerNeeded = false;
248                                }
249
250                                String stringValue = p.serialize(value);
251
252                                sink.print(p.getId()).print(":").print(stringValue);
253                                // p.save(sink, stringValue);
254                                sink.breakLine();
255                        }
256                        if (!headerNeeded) {
257                                sink.breakLine();
258                        }
259                        return;
260                }
261                throw new FramsticksException().msg("invalid type of access for primitive save").arg("access", access);
262        }
263
264        public static void save(Access access, Sink sink) {
265                if (access instanceof ObjectAccess) {
266                        savePrimitives(access, sink);
267                        return;
268                }
269                if (access instanceof ListAccess) {
270                        ListAccess listAccess = (ListAccess) access;
271                        for (CompositeParam p : filterInstanceof(listAccess.getParams(), CompositeParam.class)) {
272                                Object child = listAccess.get(p, Object.class);
273                                //this is probably an assertion
274                                assert child != null;
275                                save(listAccess.getElementAccess().select(child), sink);
276                        }
277                        return;
278                }
279                throw new FramsticksException().msg("unknown access category").arg("access", access);
280        }
281
282        public static void loadComposites(Access access, Source source, final Registry registry) {
283                if (access instanceof ObjectAccess) {
284                        final ObjectAccess objectAccess = (ObjectAccess) access;
285
286                        MultiParamLoader loader = new MultiParamLoader();
287
288                        loader.setNewSource(source);
289
290                        loader.setAccessProvider(new AccessProvider() {
291                                @Override
292                                public Access getAccess(String name) {
293                                        CompositeParam result = null;
294                                        for (CompositeParam p : filterInstanceof(objectAccess.getParams(), CompositeParam.class)) {
295                                                if (p.getContainedTypeName().equals(name)) {
296                                                        if (result != null) {
297                                                                throw new FramsticksException().msg("class name is ambiguous in access").arg("name", name).arg("first candidate", result).arg("second candidate", p);
298                                                        }
299                                                        result = p;
300
301                                                }
302                                        }
303                                        if (result == null) {
304                                                throw new FramsticksException().msg("class name is unknown").arg("name", name).arg("in", objectAccess);
305                                        }
306
307                                        return registry.prepareAccess(result, true).select(objectAccess.get(result, Object.class));
308                                }
309                        });
310
311
312                        loader.go();
313
314                        return;
315                }
316                throw new UnimplementedException().msg("unknown access category").arg("access", access);
317        }
318
319        public static void load(Access access, Source source) {
320                if (!(access instanceof ObjectAccess)) {
321                        throw new FramsticksException().msg("access is not an object access").arg("access", access);
322                }
323                Entry entry;
324                while ((entry = readEntry(source)) != null) {
325                        Param param = access.getParam(entry.key);
326                        if (param == null) {
327                                throw new FramsticksException().msg("param not found in access").arg("name", entry.key).arg("access", access);
328                        }
329                        if (!(param instanceof ValueParam)) {
330                                throw new FramsticksException().msg("param is not a value param").arg("param", param).arg("access", access);
331                        }
332                        if ((param.getFlags() & ParamFlags.DONTLOAD) == 0) {
333                                ValueParam valueParam = (ValueParam) param;
334                                // Object object = valueParam.deserialize(entry.value, null,  Object.class);
335                                int retFlags = access.set(valueParam, entry.value);
336                                if ((retFlags & (PSET_HITMIN | PSET_HITMAX)) != 0) {
337                                        String which = ((retFlags & PSET_HITMIN) != 0) ? "small" : "big";
338                                        log.warn("value of key '{}' was too {}, adjusted", entry.key, which);
339                                }
340                        }
341                }
342        }
343
344        public interface Adjuster {
345                public Holder<Object> adjust(ValueParam param);
346                public Class<? extends ValueParam> getParamType();
347        }
348
349        public static class MinAdjuster implements Adjuster {
350
351                /**
352                 *
353                 */
354                public MinAdjuster() {
355                }
356
357                @Override
358                public Class<? extends ValueParam> getParamType() {
359                        return PrimitiveParam.class;
360                }
361
362                @Override
363                public Holder<Object> adjust(ValueParam param) {
364                        Object value = ((PrimitiveParam<?>) param).getMin(Object.class);
365                        if (value == null) {
366                                return null;
367                        }
368                        return Holder.make(value);
369                }
370        }
371
372        public static class MaxAdjuster implements Adjuster {
373
374                /**
375                 *
376                 */
377                public MaxAdjuster() {
378                }
379
380                @Override
381                public Class<? extends ValueParam> getParamType() {
382                        return PrimitiveParam.class;
383                }
384
385                @Override
386                public Holder<Object> adjust(ValueParam param) {
387                        Object value = ((PrimitiveParam<?>) param).getMax(Object.class);
388                        if (value == null) {
389                                return null;
390                        }
391                        return Holder.make(value);
392                }
393        }
394
395        public static class DefAdjuster implements Adjuster {
396
397                protected final boolean numericOnly;
398
399                /**
400                 * @param numericOnly
401                 */
402                public DefAdjuster(boolean numericOnly) {
403                        this.numericOnly = numericOnly;
404                }
405
406                public Class<? extends ValueParam> getParamType() {
407                        return ValueParam.class;
408                }
409
410                @Override
411                public Holder<Object> adjust(ValueParam param) {
412                        if (numericOnly && !(param.isNumeric())) {
413                                return null;
414                        }
415                        return Holder.make(param.getDef(Object.class));
416                }
417        }
418
419        public static void adjustAll(Access access, Adjuster adjuster) {
420                for (ValueParam param : Containers.filterInstanceof(access.getParams(), adjuster.getParamType())) {
421                        Holder<Object> value = adjuster.adjust(param);
422                        if (value != null) {
423                                access.set(param, value.get());
424                        }
425                }
426        }
427
428        public static Object wrapValueInResultIfPrimitive(Object object) {
429                Class<?> javaClass = object.getClass();
430                if (ClassUtils.isPrimitiveOrWrapper(javaClass)) {
431                        return new Result(object);
432                }
433                if (javaClass.equals(String.class)) {
434                        return new Result(object);
435                }
436                return object;
437        }
438
439        /**
440         *
441         * If both arguments are File, than do nothing; otherwise:
442         *
443         * If from argument is a File:
444         * - if toJavaClass is Object.class, than try read using registry
445         * - otherwise: try use loadComposites
446         *
447         * If to argument is a File:
448         * - use Registry to saveAll
449         *
450         */
451        public static <T, F> T convert(Class<T> toJavaClass, F from, Registry registry) {
452                log.trace("converting from {} to {}", from, toJavaClass);
453                if (toJavaClass.equals(from.getClass())) {
454                        return toJavaClass.cast(from);
455                }
456                if (from instanceof File) {
457                        File file = (File) from;
458                        return Casting.throwCast(toJavaClass, loadAll((toJavaClass.equals(Object.class) ? null : registry.createAccess(toJavaClass)), file.getContent(), registry).getSelected());
459                }
460                if (toJavaClass.equals(File.class)) {
461                        ListSink sink = new ListSink();
462                        saveAll(registry.createAccess(from.getClass()).select(from), sink, registry);
463                        return Casting.throwCast(toJavaClass, new File("", new ListSource(sink.getOut())));
464                }
465
466                throw new FramsticksUnsupportedOperationException().msg("conversion").arg("from", from.getClass()).arg("to", toJavaClass);
467        }
468
469        @SuppressWarnings("serial")
470        public static class EqualityException extends FramsticksException {
471        }
472
473
474        public static void assureEquality(Access a, Access b, Registry registry) {
475                try {
476                        if (a.getParamCount() != b.getParamCount()) {
477                                throw new EqualityException().msg("param count not equal").arg("left", a.getParamCount()).arg("right", b.getParamCount());
478                        }
479                        for (ValueParam avp : Containers.filterInstanceof(a.getParams(), ValueParam.class)) {
480                                Param bp = b.getParam(avp.getId());
481                                if (bp == null) {
482                                        throw new EqualityException().msg("param from left not present in right").arg("param", avp);
483                                }
484                                Misc.checkEquals(avp.getClass(), bp.getClass(), "params type not equals", null);
485                                ValueParam bvp = (ValueParam) bp;
486
487                                Object oa = a.get(avp, Object.class);
488                                Object ob = b.get(avp, Object.class);
489
490                                if (avp instanceof CompositeParam) {
491                                        assureEquality(registry.prepareAccess((CompositeParam) avp, false).select(oa), registry.prepareAccess((CompositeParam) bvp, false).select(ob), registry);
492                                        continue;
493                                }
494                                Misc.checkEquals(oa, ob, "values not equal", null);
495                        }
496                } catch (EqualityException e) {
497                        throw e.arg("left", a).arg("right", b);
498                }
499        }
500
501        public static boolean areEqual(Access a, Access b, Registry registry) {
502                try {
503                        assureEquality(a, b, registry);
504                        return true;
505                } catch (EqualityException e) {
506                }
507                return false;
508        }
509
510}
Note: See TracBrowser for help on using the repository browser.