[97] | 1 | package com.framsticks.core; |
---|
| 2 | |
---|
[99] | 3 | import java.util.HashSet; |
---|
[97] | 4 | import java.util.List; |
---|
[99] | 5 | import java.util.Set; |
---|
[97] | 6 | |
---|
| 7 | import javax.annotation.Nonnull; |
---|
| 8 | |
---|
[100] | 9 | import org.apache.logging.log4j.Logger; |
---|
| 10 | import org.apache.logging.log4j.LogManager; |
---|
[97] | 11 | |
---|
| 12 | import com.framsticks.communication.File; |
---|
[100] | 13 | import com.framsticks.params.Access; |
---|
| 14 | import com.framsticks.params.CompositeParam; |
---|
[99] | 15 | import com.framsticks.params.EventListener; |
---|
[97] | 16 | import com.framsticks.params.FramsClass; |
---|
| 17 | import com.framsticks.params.ListAccess; |
---|
| 18 | import com.framsticks.params.Param; |
---|
[100] | 19 | import com.framsticks.params.ParamBuilder; |
---|
[97] | 20 | import com.framsticks.params.PrimitiveParam; |
---|
[100] | 21 | import com.framsticks.params.PropertiesAccess; |
---|
[99] | 22 | import com.framsticks.params.UniqueListAccess; |
---|
[101] | 23 | import com.framsticks.params.ParamsUtil; |
---|
[99] | 24 | import com.framsticks.params.types.EventParam; |
---|
[97] | 25 | import com.framsticks.params.types.ObjectParam; |
---|
| 26 | import com.framsticks.params.types.ProcedureParam; |
---|
| 27 | import com.framsticks.parsers.Loaders; |
---|
| 28 | import com.framsticks.parsers.MultiParamLoader; |
---|
| 29 | import com.framsticks.util.FramsticksException; |
---|
[100] | 30 | import com.framsticks.util.dispatching.Dispatching; |
---|
[97] | 31 | import com.framsticks.util.dispatching.Future; |
---|
| 32 | import com.framsticks.util.dispatching.FutureHandler; |
---|
| 33 | import com.framsticks.util.dispatching.RunAt; |
---|
| 34 | |
---|
| 35 | import static com.framsticks.util.dispatching.Dispatching.*; |
---|
| 36 | |
---|
| 37 | public final class TreeOperations { |
---|
| 38 | |
---|
[100] | 39 | private static final Logger log = LogManager.getLogger(TreeOperations.class); |
---|
[97] | 40 | |
---|
| 41 | private TreeOperations() { |
---|
| 42 | } |
---|
| 43 | |
---|
[101] | 44 | public static final SideNoteKey<Boolean> FETCHED_MARK = SideNoteKey.make(Boolean.class); |
---|
[100] | 45 | |
---|
[102] | 46 | public static @Nonnull FramsClass processFetchedInfo(Tree tree, File file) { |
---|
[97] | 47 | assert tree.isActive(); |
---|
| 48 | FramsClass framsClass = Loaders.loadFramsClass(file.getContent()); |
---|
[100] | 49 | log.debug("process fetched info for {}: {}", tree, framsClass); |
---|
[97] | 50 | tree.putInfoIntoCache(framsClass); |
---|
| 51 | return framsClass; |
---|
| 52 | } |
---|
| 53 | |
---|
[100] | 54 | public static Path create(Path path) { |
---|
| 55 | assert !path.isResolved(); |
---|
[97] | 56 | |
---|
[100] | 57 | Access access = path.getTree().prepareAccess(path.getTop().getParam()); |
---|
| 58 | Object child = createAccessee(path.getTree(), access); |
---|
| 59 | assert child != null; |
---|
| 60 | if (path.size() == 1) { |
---|
| 61 | path.getTree().assignRootObject(child); |
---|
| 62 | } else { |
---|
| 63 | Access parentAccess = bindAccess(path.getUnder()); |
---|
| 64 | |
---|
| 65 | /** this special case is not very good - maybe hide it in createAccessee? */ |
---|
| 66 | if (parentAccess instanceof UniqueListAccess) { |
---|
| 67 | access.select(child); |
---|
| 68 | access.set(((UniqueListAccess) parentAccess).getUidName(), path.getTop().getParam().getId()); |
---|
[97] | 69 | } |
---|
[100] | 70 | |
---|
| 71 | parentAccess.set(path.getTop().getParam(), child); |
---|
[97] | 72 | } |
---|
[100] | 73 | path = path.appendResolution(child); |
---|
| 74 | return path; |
---|
| 75 | } |
---|
[97] | 76 | |
---|
[100] | 77 | public static void processFetchedValues(final Path path, final List<File> files, final Access access, final Future<Path> future) { |
---|
| 78 | assert files.size() == 1; |
---|
| 79 | assert path.isTheSame(files.get(0).getPath()); |
---|
[97] | 80 | |
---|
| 81 | try { |
---|
[100] | 82 | log.debug("process fetched values: {}", path); |
---|
| 83 | final Access parsingAccess = new PropertiesAccess(access.getFramsClass()); |
---|
| 84 | final List<Object> results = MultiParamLoader.loadAll(files.get(0).getContent(), parsingAccess); |
---|
[97] | 85 | |
---|
[100] | 86 | Dispatching.dispatchIfNotActive(path.getTree(), new RunAt<Tree>(future) { |
---|
| 87 | @Override |
---|
| 88 | protected void runAt() { |
---|
[97] | 89 | |
---|
[100] | 90 | Path result = path.tryResolveIfNeeded(); |
---|
[99] | 91 | |
---|
[100] | 92 | if (!result.isResolved()) { |
---|
| 93 | result = create(result); |
---|
| 94 | } |
---|
[99] | 95 | |
---|
[100] | 96 | if (path.getTop().getParam() instanceof ObjectParam) { |
---|
| 97 | assert results.size() == 1; |
---|
[101] | 98 | ParamsUtil.takeAllNonNullValues(bindAccess(result), parsingAccess.select(results.get(0))); |
---|
[100] | 99 | mark(result.getTree(), result.getTopObject(), FETCHED_MARK, true); |
---|
| 100 | future.pass(result); |
---|
| 101 | return; |
---|
| 102 | } |
---|
[99] | 103 | |
---|
[100] | 104 | final ListAccess listAccess = (ListAccess) access; |
---|
| 105 | |
---|
| 106 | listAccess.select(result.getTopObject()); |
---|
| 107 | Set<String> oldValuesIds = new HashSet<>(); |
---|
| 108 | for (Param p : listAccess.getParams()) { |
---|
| 109 | oldValuesIds.add(p.getId()); |
---|
[99] | 110 | } |
---|
| 111 | |
---|
[100] | 112 | Access targetAccess = listAccess.getElementAccess();//.cloneAccess(); |
---|
| 113 | |
---|
| 114 | int number = 0; |
---|
| 115 | for (Object r : results) { |
---|
| 116 | |
---|
| 117 | parsingAccess.select(r); |
---|
| 118 | String id; |
---|
| 119 | if (listAccess instanceof UniqueListAccess) { |
---|
| 120 | id = parsingAccess.get(((UniqueListAccess) listAccess).getUidName(), String.class); |
---|
| 121 | } else { |
---|
| 122 | id = Integer.toString(number); |
---|
| 123 | } |
---|
| 124 | ++number; |
---|
| 125 | |
---|
| 126 | Object childTo = listAccess.get(id, Object.class); |
---|
| 127 | boolean newOne; |
---|
| 128 | if (childTo == null) { |
---|
| 129 | childTo = createAccessee(result.getTree(), targetAccess); |
---|
| 130 | newOne = true; |
---|
| 131 | } else { |
---|
| 132 | assert oldValuesIds.contains(id); |
---|
| 133 | newOne = false; |
---|
| 134 | } |
---|
| 135 | oldValuesIds.remove(id); |
---|
| 136 | |
---|
| 137 | targetAccess.select(childTo); |
---|
[101] | 138 | ParamsUtil.takeAllNonNullValues(targetAccess, parsingAccess); |
---|
[100] | 139 | if (newOne) { |
---|
| 140 | listAccess.set(id, childTo); |
---|
| 141 | } |
---|
| 142 | mark(result.getTree(), childTo, FETCHED_MARK, true); |
---|
| 143 | |
---|
[99] | 144 | } |
---|
[100] | 145 | mark(result.getTree(), result.getTopObject(), FETCHED_MARK, true); |
---|
| 146 | |
---|
| 147 | /** It looks tricky for ArrayListAccess but should also work. |
---|
| 148 | * |
---|
| 149 | * They should be sorted. |
---|
| 150 | */ |
---|
| 151 | for (String id : oldValuesIds) { |
---|
| 152 | listAccess.set(id, null); |
---|
[99] | 153 | } |
---|
[100] | 154 | future.pass(result); |
---|
[97] | 155 | } |
---|
[100] | 156 | }); |
---|
[97] | 157 | |
---|
[98] | 158 | } catch (FramsticksException e) { |
---|
| 159 | throw new FramsticksException().msg("exception occurred while loading").cause(e); |
---|
[97] | 160 | } |
---|
| 161 | } |
---|
| 162 | |
---|
[98] | 163 | public static FramsClass getInfo(Path path) { |
---|
| 164 | Tree tree = path.getTree(); |
---|
| 165 | assert tree.isActive(); |
---|
[100] | 166 | log.debug("get info for: {}", path); |
---|
[98] | 167 | final String name = path.getTop().getParam().getContainedTypeName(); |
---|
| 168 | return tree.getInfoFromCache(name); |
---|
[97] | 169 | } |
---|
| 170 | |
---|
[98] | 171 | public static void findInfo(final Path path, final Future<FramsClass> future) { |
---|
[100] | 172 | log.debug("find info for: {}", path); |
---|
[98] | 173 | try { |
---|
| 174 | Tree tree = path.getTree(); |
---|
| 175 | assert tree.isActive(); |
---|
| 176 | final FramsClass framsClass = getInfo(path); |
---|
| 177 | if (framsClass != null) { |
---|
| 178 | future.pass(framsClass); |
---|
[97] | 179 | return; |
---|
| 180 | } |
---|
[98] | 181 | tree.info(path, future); |
---|
| 182 | } catch (FramsticksException e) { |
---|
| 183 | future.handle(e); |
---|
[97] | 184 | } |
---|
| 185 | } |
---|
| 186 | |
---|
[101] | 187 | public static @Nonnull |
---|
| 188 | Access bindAccessFromSideNote(Tree tree, Object object) { |
---|
| 189 | CompositeParam param = tree.getSideNote(object, Path.OBJECT_PARAM_KEY); |
---|
[100] | 190 | if (param == null) { |
---|
| 191 | throw new FramsticksException().msg("failed to bind access from side node").arg("tree", tree).arg("object", object).arg("type", object.getClass()); |
---|
| 192 | } |
---|
| 193 | return tree.prepareAccess(param).select(object); |
---|
| 194 | } |
---|
[97] | 195 | |
---|
[101] | 196 | public static @Nonnull |
---|
| 197 | Access bindAccess(Tree tree, String path) { |
---|
[100] | 198 | log.debug("bind access for textual: {} in {}", path, tree); |
---|
[97] | 199 | return bindAccess(Path.to(tree, path)); |
---|
| 200 | } |
---|
| 201 | |
---|
[101] | 202 | public static @Nonnull |
---|
| 203 | Access bindAccess(Node node) { |
---|
[98] | 204 | Tree tree = node.getTree(); |
---|
[97] | 205 | assert tree.isActive(); |
---|
| 206 | assert node.getObject() != null; |
---|
| 207 | |
---|
| 208 | try { |
---|
[100] | 209 | Access access = tree.prepareAccess(node.getParam()); |
---|
[101] | 210 | tree.putSideNote(node.getObject(), Path.OBJECT_PARAM_KEY, node.getParam()); |
---|
[100] | 211 | |
---|
| 212 | return access.select(node.getObject()); |
---|
[97] | 213 | } catch (FramsticksException e) { |
---|
| 214 | throw new FramsticksException().msg("failed to prepare access for param").arg("param", node.getParam()).cause(e); |
---|
[100] | 215 | // log.error("failed to bind access for {}: ", node.getParam(), e); |
---|
[97] | 216 | } |
---|
| 217 | } |
---|
| 218 | |
---|
[101] | 219 | public static @Nonnull |
---|
| 220 | Access bindAccess(Path path) { |
---|
[97] | 221 | assert path.getTree().isActive(); |
---|
| 222 | path.assureResolved(); |
---|
[100] | 223 | log.debug("bind access for: {}", path); |
---|
[98] | 224 | return bindAccess(path.getTop()); |
---|
[97] | 225 | } |
---|
| 226 | |
---|
| 227 | public static void set(final Path path, final PrimitiveParam<?> param, final Object value, final Future<Integer> future) { |
---|
| 228 | final Tree tree = path.getTree(); |
---|
| 229 | |
---|
| 230 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 231 | @Override |
---|
| 232 | protected void runAt() { |
---|
| 233 | tree.set(path, param, value, future); |
---|
| 234 | } |
---|
| 235 | }); |
---|
| 236 | } |
---|
| 237 | |
---|
[103] | 238 | public static <R> void call(final Path path, final String procedureName, final Object[] arguments, final Class<R> resultType, final Future<R> future) { |
---|
[100] | 239 | final Tree tree = path.getTree(); |
---|
| 240 | |
---|
| 241 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 242 | @Override |
---|
| 243 | protected void runAt() { |
---|
| 244 | path.assureResolved(); |
---|
[103] | 245 | tree.call(path, tree.getRegistry().getFramsClass(path.getTop().getParam()).getParamEntry(procedureName, ProcedureParam.class), arguments, resultType, future); |
---|
[100] | 246 | } |
---|
| 247 | }); |
---|
| 248 | } |
---|
| 249 | |
---|
[103] | 250 | public static <R> void call(final Path path, final ProcedureParam param, final Object[] arguments, final Class<R> resultType, final Future<R> future) { |
---|
[97] | 251 | final Tree tree = path.getTree(); |
---|
| 252 | |
---|
| 253 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 254 | @Override |
---|
| 255 | protected void runAt() { |
---|
[103] | 256 | tree.call(path, param, arguments, resultType, future); |
---|
[97] | 257 | } |
---|
| 258 | }); |
---|
| 259 | } |
---|
| 260 | |
---|
[99] | 261 | public static <A> void addListener(final Path path, final EventParam param, final EventListener<A> listener, final Class<A> argument, final Future<Void> future) { |
---|
| 262 | final Tree tree = path.getTree(); |
---|
[98] | 263 | |
---|
[99] | 264 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 265 | @Override |
---|
| 266 | protected void runAt() { |
---|
| 267 | tree.addListener(path, param, listener, argument, future); |
---|
| 268 | } |
---|
| 269 | }); |
---|
| 270 | } |
---|
| 271 | |
---|
| 272 | public static void removeListener(final Path path, final EventParam param, final EventListener<?> listener, final Future<Void> future) { |
---|
| 273 | final Tree tree = path.getTree(); |
---|
| 274 | |
---|
| 275 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 276 | @Override |
---|
| 277 | protected void runAt() { |
---|
| 278 | tree.removeListener(path, param, listener, future); |
---|
| 279 | } |
---|
| 280 | }); |
---|
| 281 | } |
---|
| 282 | |
---|
| 283 | /** |
---|
| 284 | * |
---|
| 285 | * If StackOverflow occurs in that loop in LocalTree it is probably caused |
---|
| 286 | * the by the fact, that get operation may find the object, but Path resolution |
---|
| 287 | * cannot. |
---|
| 288 | * */ |
---|
[98] | 289 | public static void tryGet(final Tree tree, final String targetPath, final Future<Path> future) { |
---|
[100] | 290 | log.debug("resolve textual: {} for {}", targetPath, tree); |
---|
[97] | 291 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 292 | |
---|
| 293 | @Override |
---|
| 294 | protected void runAt() { |
---|
[99] | 295 | final Path path = Path.tryTo(tree, targetPath).tryResolveIfNeeded(); |
---|
[100] | 296 | log.debug("found: {}", path); |
---|
[98] | 297 | if (path.isResolved()) { |
---|
| 298 | future.pass(path); |
---|
| 299 | return; |
---|
| 300 | } |
---|
| 301 | |
---|
[99] | 302 | tree.get(path, new FutureHandler<Path>(future) { |
---|
[97] | 303 | @Override |
---|
| 304 | protected void result(Path result) { |
---|
[99] | 305 | // if (result.isResolved(targetPath)) { |
---|
[100] | 306 | // future.pass(result); |
---|
| 307 | // return; |
---|
[99] | 308 | // } |
---|
[100] | 309 | log.debug("retrying resolve textual: {} for {} with {}", targetPath, tree, result); |
---|
[98] | 310 | tryGet(tree, targetPath, future); |
---|
[97] | 311 | } |
---|
| 312 | }); |
---|
| 313 | } |
---|
| 314 | }); |
---|
| 315 | } |
---|
| 316 | |
---|
| 317 | public static FramsClass getInfoFromCache(Path path) { |
---|
| 318 | assert path.getTree().isActive(); |
---|
| 319 | return path.getTree().getInfoFromCache(path.getTop().getParam().getContainedTypeName()); |
---|
| 320 | } |
---|
[99] | 321 | |
---|
[100] | 322 | public static Object createAccessee(Tree tree, CompositeParam param) { |
---|
| 323 | Object object = tree.prepareAccess(param).createAccessee(); |
---|
[101] | 324 | tree.putSideNote(object, Path.OBJECT_PARAM_KEY, param); |
---|
[100] | 325 | return object; |
---|
| 326 | } |
---|
| 327 | |
---|
| 328 | public static Object createAccessee(Tree tree, Access access) { |
---|
| 329 | Object object = access.createAccessee(); |
---|
[101] | 330 | tree.putSideNote(object, Path.OBJECT_PARAM_KEY, access.buildParam(new ParamBuilder()).finish(CompositeParam.class)); |
---|
[100] | 331 | return object; |
---|
| 332 | } |
---|
| 333 | |
---|
[101] | 334 | public static boolean isMarked(Tree tree, Object object, SideNoteKey<Boolean> mark, boolean defValue) { |
---|
[100] | 335 | assert tree.isActive(); |
---|
[101] | 336 | Boolean v = tree.getSideNote(object, mark); |
---|
[100] | 337 | return (v != null ? v : defValue); |
---|
| 338 | } |
---|
| 339 | |
---|
[101] | 340 | public static void mark(Tree tree, Object object, SideNoteKey<Boolean> mark, boolean value) { |
---|
[100] | 341 | assert tree.isActive(); |
---|
| 342 | tree.putSideNote(object, mark, value); |
---|
| 343 | } |
---|
| 344 | |
---|
| 345 | public static FramsClass getFramsClass(Path path) { |
---|
| 346 | return path.getTree().getRegistry().getFramsClass(path.getTop().getParam()); |
---|
| 347 | } |
---|
| 348 | |
---|
[101] | 349 | public static <T extends Param> T getParam(Path path, String id, Class<T> type) { |
---|
| 350 | return getFramsClass(path).getParamEntry(id, type); |
---|
| 351 | } |
---|
| 352 | |
---|
| 353 | public static <T> T getOrCreateSideNote(Tree tree, Object object, SideNoteKey<T> key) { |
---|
| 354 | T result = tree.getSideNote(object, key); |
---|
| 355 | if (result == null) { |
---|
| 356 | result = key.newValue(); |
---|
| 357 | tree.putSideNote(object, key, result); |
---|
| 358 | } |
---|
| 359 | return result; |
---|
| 360 | } |
---|
| 361 | |
---|
| 362 | public static <T> boolean hasSideNotes(Tree tree, Object object, SideNoteKey<T> key) { |
---|
| 363 | return tree.getSideNote(object, key) != null; |
---|
| 364 | } |
---|
| 365 | |
---|
| 366 | public static <T> boolean hasSideNote(Path path, SideNoteKey<T> key) { |
---|
| 367 | return path.getTree().getSideNote(path.getTopObject(), key) != null; |
---|
| 368 | } |
---|
| 369 | |
---|
| 370 | public static <T> T getSideNote(Path path, SideNoteKey<T> key) { |
---|
| 371 | assert path.isResolved(); |
---|
| 372 | return path.getTree().getSideNote(path.getTopObject(), key); |
---|
| 373 | } |
---|
| 374 | |
---|
| 375 | public static <T> void putSideNote(Path path, SideNoteKey<T> key, T value) { |
---|
| 376 | path.getTree().putSideNote(path.getTopObject(), key, value); |
---|
| 377 | } |
---|
| 378 | |
---|
| 379 | public static boolean removeSideNote(Path path, SideNoteKey<?> key) { |
---|
| 380 | assert path.isResolved(); |
---|
| 381 | return path.getTree().removeSideNote(path.getTopObject(), key); |
---|
| 382 | } |
---|
| 383 | |
---|
[97] | 384 | } |
---|