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