[97] | 1 | package com.framsticks.core; |
---|
| 2 | |
---|
[98] | 3 | |
---|
[99] | 4 | import java.util.HashSet; |
---|
[97] | 5 | import java.util.List; |
---|
[99] | 6 | import java.util.Set; |
---|
[97] | 7 | |
---|
| 8 | import javax.annotation.Nonnull; |
---|
| 9 | |
---|
| 10 | import org.apache.log4j.Logger; |
---|
| 11 | |
---|
| 12 | import com.framsticks.communication.File; |
---|
| 13 | import com.framsticks.params.AccessInterface; |
---|
[99] | 14 | import com.framsticks.params.EventListener; |
---|
[97] | 15 | import com.framsticks.params.FramsClass; |
---|
| 16 | import com.framsticks.params.ListAccess; |
---|
| 17 | import com.framsticks.params.Param; |
---|
| 18 | import com.framsticks.params.PrimitiveParam; |
---|
[99] | 19 | import com.framsticks.params.UniqueListAccess; |
---|
| 20 | import com.framsticks.params.Util; |
---|
| 21 | import com.framsticks.params.types.EventParam; |
---|
[97] | 22 | import com.framsticks.params.types.ObjectParam; |
---|
| 23 | import com.framsticks.params.types.ProcedureParam; |
---|
| 24 | import com.framsticks.parsers.Loaders; |
---|
| 25 | import com.framsticks.parsers.MultiParamLoader; |
---|
| 26 | import com.framsticks.util.FramsticksException; |
---|
| 27 | import com.framsticks.util.dispatching.Future; |
---|
| 28 | import com.framsticks.util.dispatching.FutureHandler; |
---|
| 29 | import com.framsticks.util.dispatching.RunAt; |
---|
| 30 | |
---|
| 31 | import static com.framsticks.util.dispatching.Dispatching.*; |
---|
| 32 | |
---|
| 33 | public final class TreeOperations { |
---|
| 34 | |
---|
[98] | 35 | private static final Logger log = Logger.getLogger(TreeOperations.class); |
---|
[97] | 36 | |
---|
| 37 | private TreeOperations() { |
---|
| 38 | } |
---|
| 39 | |
---|
| 40 | public static @Nonnull FramsClass processFetchedInfo(Tree tree, File file) { |
---|
| 41 | assert tree.isActive(); |
---|
| 42 | FramsClass framsClass = Loaders.loadFramsClass(file.getContent()); |
---|
[98] | 43 | log.debug("process fetched info for " + tree + ": " + framsClass); |
---|
[97] | 44 | tree.putInfoIntoCache(framsClass); |
---|
| 45 | return framsClass; |
---|
| 46 | } |
---|
| 47 | |
---|
[98] | 48 | public static void processFetchedValues(Path path, List<File> files) { |
---|
[97] | 49 | Tree tree = path.getTree(); |
---|
| 50 | assert tree.isActive(); |
---|
[98] | 51 | assert files.size() == 1; |
---|
| 52 | assert path.isTheSame(files.get(0).getPath()); |
---|
[97] | 53 | |
---|
[98] | 54 | if (!path.isResolved()) { |
---|
| 55 | AccessInterface access = tree.prepareAccess(path.getTop().getParam()); |
---|
| 56 | Object child = access.createAccessee(); |
---|
| 57 | assert child != null; |
---|
| 58 | if (path.size() == 1) { |
---|
| 59 | tree.assignRootObject(child); |
---|
| 60 | } else { |
---|
| 61 | bindAccess(path.getUnder()).set(path.getTop().getParam(), child); |
---|
[97] | 62 | } |
---|
[98] | 63 | path = path.appendResolution(child); |
---|
[97] | 64 | } |
---|
| 65 | |
---|
[98] | 66 | log.debug("process fetched values: " + path); |
---|
[97] | 67 | Node node = path.getTop(); |
---|
| 68 | MultiParamLoader loader = new MultiParamLoader(); |
---|
| 69 | loader.setNewSource(files.get(0).getContent()); |
---|
| 70 | loader.addBreakCondition(MultiParamLoader.Status.AfterObject); |
---|
| 71 | |
---|
| 72 | try { |
---|
| 73 | if (node.getParam() instanceof ObjectParam) { |
---|
[98] | 74 | loader.addAccessInterface(bindAccess(node)); |
---|
[97] | 75 | loader.go(); |
---|
| 76 | return; |
---|
| 77 | } |
---|
| 78 | |
---|
[98] | 79 | ListAccess listAccess = (ListAccess) bindAccess(node); |
---|
[97] | 80 | |
---|
[99] | 81 | Set<String> oldValuesIds = new HashSet<>(); |
---|
| 82 | for (Param p : listAccess.getParams()) { |
---|
| 83 | oldValuesIds.add(p.getId()); |
---|
| 84 | } |
---|
| 85 | |
---|
| 86 | // listAccess.clearValues(); |
---|
| 87 | |
---|
[97] | 88 | AccessInterface elementAccess = listAccess.getElementAccess(); |
---|
[99] | 89 | AccessInterface clonerInterface = elementAccess.cloneAccess(); |
---|
| 90 | |
---|
[97] | 91 | loader.addAccessInterface(elementAccess); |
---|
| 92 | MultiParamLoader.Status status; |
---|
[99] | 93 | int number = 0; |
---|
[97] | 94 | while ((status = loader.go()) != MultiParamLoader.Status.Finished) { |
---|
| 95 | if (status == MultiParamLoader.Status.AfterObject) { |
---|
| 96 | AccessInterface accessInterface = loader.getLastAccessInterface(); |
---|
| 97 | |
---|
[99] | 98 | String id; |
---|
| 99 | if (listAccess instanceof UniqueListAccess) { |
---|
| 100 | id = ((UniqueListAccess) listAccess).computeIdentifierFor(accessInterface.getSelected()); |
---|
| 101 | } else { |
---|
| 102 | id = Integer.toString(number); |
---|
| 103 | } |
---|
| 104 | ++number; |
---|
| 105 | |
---|
| 106 | Object childTo = listAccess.get(id, Object.class); |
---|
| 107 | boolean newOne; |
---|
| 108 | if (childTo == null) { |
---|
| 109 | childTo = clonerInterface.createAccessee(); |
---|
| 110 | newOne = true; |
---|
| 111 | } else { |
---|
| 112 | assert oldValuesIds.contains(id); |
---|
| 113 | newOne = false; |
---|
| 114 | } |
---|
| 115 | oldValuesIds.remove(id); |
---|
| 116 | clonerInterface.select(childTo); |
---|
| 117 | Util.takeAllNonNullValues(clonerInterface, accessInterface); |
---|
| 118 | if (newOne) { |
---|
| 119 | listAccess.set(id, childTo); |
---|
| 120 | } |
---|
| 121 | |
---|
| 122 | // listAccess.set(id, accessInterface.getSelected()); |
---|
[97] | 123 | accessInterface.select(null); |
---|
[99] | 124 | |
---|
[97] | 125 | } |
---|
| 126 | } |
---|
[99] | 127 | /** It looks tricky for ArrayListAccess but should also work. |
---|
| 128 | * |
---|
| 129 | * They should be sorted. |
---|
| 130 | */ |
---|
| 131 | for (String id : oldValuesIds) { |
---|
| 132 | listAccess.set(id, null); |
---|
| 133 | } |
---|
[97] | 134 | |
---|
[98] | 135 | } catch (FramsticksException e) { |
---|
| 136 | throw new FramsticksException().msg("exception occurred while loading").cause(e); |
---|
[97] | 137 | } |
---|
| 138 | } |
---|
| 139 | |
---|
[98] | 140 | public static FramsClass getInfo(Path path) { |
---|
| 141 | Tree tree = path.getTree(); |
---|
| 142 | assert tree.isActive(); |
---|
| 143 | log.debug("get info for: " + path); |
---|
| 144 | final String name = path.getTop().getParam().getContainedTypeName(); |
---|
| 145 | return tree.getInfoFromCache(name); |
---|
[97] | 146 | } |
---|
| 147 | |
---|
[98] | 148 | public static void findInfo(final Path path, final Future<FramsClass> future) { |
---|
| 149 | log.debug("find info for: " + path); |
---|
| 150 | try { |
---|
| 151 | Tree tree = path.getTree(); |
---|
| 152 | assert tree.isActive(); |
---|
| 153 | final FramsClass framsClass = getInfo(path); |
---|
| 154 | if (framsClass != null) { |
---|
| 155 | future.pass(framsClass); |
---|
[97] | 156 | return; |
---|
| 157 | } |
---|
[98] | 158 | tree.info(path, future); |
---|
| 159 | } catch (FramsticksException e) { |
---|
| 160 | future.handle(e); |
---|
[97] | 161 | } |
---|
| 162 | } |
---|
| 163 | |
---|
| 164 | |
---|
| 165 | |
---|
| 166 | public static @Nonnull AccessInterface bindAccess(Tree tree, String path) { |
---|
[98] | 167 | log.debug("bind access for textual: " + path + " in " + tree); |
---|
[97] | 168 | return bindAccess(Path.to(tree, path)); |
---|
| 169 | } |
---|
| 170 | |
---|
[98] | 171 | public static @Nonnull AccessInterface bindAccess(Node node) { |
---|
| 172 | Tree tree = node.getTree(); |
---|
[97] | 173 | assert tree.isActive(); |
---|
| 174 | assert node.getObject() != null; |
---|
| 175 | |
---|
| 176 | try { |
---|
| 177 | return tree.prepareAccess(node.getParam()).select(node.getObject()); |
---|
| 178 | } catch (FramsticksException e) { |
---|
| 179 | throw new FramsticksException().msg("failed to prepare access for param").arg("param", node.getParam()).cause(e); |
---|
| 180 | // log.error("failed to bind access for " + node.getParam() + ": " + e); |
---|
| 181 | } |
---|
| 182 | } |
---|
| 183 | |
---|
| 184 | public static @Nonnull AccessInterface bindAccess(Path path) { |
---|
| 185 | assert path.getTree().isActive(); |
---|
| 186 | path.assureResolved(); |
---|
[98] | 187 | log.debug("bind access for: " + path); |
---|
| 188 | return bindAccess(path.getTop()); |
---|
[97] | 189 | } |
---|
| 190 | |
---|
| 191 | public static void set(final Path path, final PrimitiveParam<?> param, final Object value, final Future<Integer> future) { |
---|
| 192 | final Tree tree = path.getTree(); |
---|
| 193 | |
---|
| 194 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 195 | @Override |
---|
| 196 | protected void runAt() { |
---|
| 197 | tree.set(path, param, value, future); |
---|
| 198 | } |
---|
| 199 | }); |
---|
| 200 | } |
---|
| 201 | |
---|
| 202 | public static void call(final Path path, final ProcedureParam param, final Object[] arguments, final Future<Object> future) { |
---|
| 203 | final Tree tree = path.getTree(); |
---|
| 204 | |
---|
| 205 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 206 | @Override |
---|
| 207 | protected void runAt() { |
---|
| 208 | tree.call(path, param, arguments, future); |
---|
| 209 | } |
---|
| 210 | }); |
---|
| 211 | } |
---|
| 212 | |
---|
[99] | 213 | public static <A> void addListener(final Path path, final EventParam param, final EventListener<A> listener, final Class<A> argument, final Future<Void> future) { |
---|
| 214 | final Tree tree = path.getTree(); |
---|
[98] | 215 | |
---|
[99] | 216 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 217 | @Override |
---|
| 218 | protected void runAt() { |
---|
| 219 | tree.addListener(path, param, listener, argument, future); |
---|
| 220 | } |
---|
| 221 | }); |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | public static void removeListener(final Path path, final EventParam param, final EventListener<?> listener, final Future<Void> future) { |
---|
| 225 | final Tree tree = path.getTree(); |
---|
| 226 | |
---|
| 227 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 228 | @Override |
---|
| 229 | protected void runAt() { |
---|
| 230 | tree.removeListener(path, param, listener, future); |
---|
| 231 | } |
---|
| 232 | }); |
---|
| 233 | } |
---|
| 234 | |
---|
| 235 | |
---|
| 236 | /** |
---|
| 237 | * |
---|
| 238 | * If StackOverflow occurs in that loop in LocalTree it is probably caused |
---|
| 239 | * the by the fact, that get operation may find the object, but Path resolution |
---|
| 240 | * cannot. |
---|
| 241 | * */ |
---|
[98] | 242 | public static void tryGet(final Tree tree, final String targetPath, final Future<Path> future) { |
---|
| 243 | log.debug("resolve textual: " + targetPath + " for " + tree); |
---|
[97] | 244 | dispatchIfNotActive(tree, new RunAt<Tree>(future) { |
---|
| 245 | |
---|
| 246 | @Override |
---|
| 247 | protected void runAt() { |
---|
[99] | 248 | final Path path = Path.tryTo(tree, targetPath).tryResolveIfNeeded(); |
---|
| 249 | log.debug("found: " + path); |
---|
[98] | 250 | if (path.isResolved()) { |
---|
| 251 | future.pass(path); |
---|
| 252 | return; |
---|
| 253 | } |
---|
| 254 | |
---|
[99] | 255 | tree.get(path, new FutureHandler<Path>(future) { |
---|
[97] | 256 | @Override |
---|
| 257 | protected void result(Path result) { |
---|
[99] | 258 | // if (result.isResolved(targetPath)) { |
---|
| 259 | // future.pass(result); |
---|
| 260 | // return; |
---|
| 261 | // } |
---|
| 262 | log.debug("retrying resolve textual: " + targetPath + " for " + tree + " with " + result); |
---|
[98] | 263 | tryGet(tree, targetPath, future); |
---|
[97] | 264 | } |
---|
| 265 | }); |
---|
| 266 | } |
---|
| 267 | }); |
---|
| 268 | } |
---|
| 269 | |
---|
| 270 | public static FramsClass getInfoFromCache(Path path) { |
---|
| 271 | assert path.getTree().isActive(); |
---|
| 272 | return path.getTree().getInfoFromCache(path.getTop().getParam().getContainedTypeName()); |
---|
| 273 | } |
---|
[99] | 274 | |
---|
[97] | 275 | } |
---|