OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 144 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 144 Rev 151
Line 121... Line 121...
121
 
121
 
122
    private final DBSystemRoot getSystemRoot() {
122
    private final DBSystemRoot getSystemRoot() {
123
        return this.getHead().getTable().getDBSystemRoot();
123
        return this.getHead().getTable().getDBSystemRoot();
124
    }
124
    }
125
 
125
 
-
 
126
    public final int getLinksCount() {
-
 
127
        return this.links.size();
-
 
128
    }
-
 
129
 
126
    /**
130
    /**
127
     * All the rowValues in this cluster.
131
     * All the rowValues in this cluster.
128
     * 
132
     * 
129
     * @return the set of SQLRowValues.
133
     * @return the set of SQLRowValues.
130
     */
134
     */
Line 323... Line 327...
323
    final SQLRowValues deepCopy(SQLRowValues v, final boolean freeze) {
327
    final SQLRowValues deepCopy(SQLRowValues v, final boolean freeze) {
324
        return deepCopy(freeze).get(v);
328
        return deepCopy(freeze).get(v);
325
    }
329
    }
326
 
330
 
327
    public final Map<SQLRowValues, SQLRowValues> deepCopy(final boolean freeze) {
331
    public final Map<SQLRowValues, SQLRowValues> deepCopy(final boolean freeze) {
-
 
332
        return this.copy(null, false, freeze);
-
 
333
    }
-
 
334
 
-
 
335
    /**
-
 
336
     * Copy a subset of this graph. For each link to copy, if the destination was copied then the
-
 
337
     * new row will point to it, otherwise the new row will point to the original row. For example,
-
 
338
     * if
-
 
339
     * 
-
 
340
     * <pre>
-
 
341
     * start is CONTAINER <-- *ITEM [F1, F2]* --> PRIVATE
-
 
342
     * and graph is ITEM [F2, ID_PRIVATE]
-
 
343
     * then after this method :
-
 
344
     *  CONTAINER <-- ITEM [F1, F2] --> PRIVATE
-
 
345
     *            \-- ITEM [F2]     --> PRIVATE
-
 
346
     * </pre>
-
 
347
     * 
-
 
348
     * @param start where to start copying.
-
 
349
     * @param graph which rows and which fields to copy, not <code>null</code>.
-
 
350
     * @return the new rows, indexed by the original rows in this instance.
-
 
351
     */
-
 
352
    public final Map<SQLRowValues, SQLRowValues> copy(final SQLRowValues start, final SQLRowValues graph) {
-
 
353
        return this.copy(computeToRetain(start, graph, true), true, false);
-
 
354
    }
-
 
355
 
-
 
356
    /**
-
 
357
     * Copy rows of this graph.
-
 
358
     * 
-
 
359
     * @param subset which rows and which fields to copy, <code>null</code> to copy all rows.
-
 
360
     * @param allowSameGraph used when copied rows point to rows which weren't copied,
-
 
361
     *        <code>true</code> to point to the original rows (and thus linking the new rows into
-
 
362
     *        the existing graph), <code>false</code> to flatten the link (like
-
 
363
     *        {@link ForeignCopyMode#COPY_ID_OR_RM}).
-
 
364
     * @param freeze <code>true</code> if the copied rows should be frozen.
-
 
365
     * @return the new rows, indexed by the original rows in this instance.
-
 
366
     */
-
 
367
    private final Map<SQLRowValues, SQLRowValues> copy(final SetMap<SQLRowValues, String> subset, final boolean allowSameGraph, final boolean freeze) {
-
 
368
        assert !allowSameGraph || !freeze;
328
        // copy all rowValues of this graph
369
        // copy all rowValues of this graph
329
        final Map<SQLRowValues, SQLRowValues> noLinkCopy = new IdentityHashMap<SQLRowValues, SQLRowValues>();
370
        final Map<SQLRowValues, SQLRowValues> noLinkCopy = new IdentityHashMap<SQLRowValues, SQLRowValues>();
330
        // don't copy foreigns, but we want to preserve the order of all fields. This works because
371
        // don't copy foreigns, but we want to preserve the order of all fields. This works because
331
        // the second put() with the actual foreign row doesn't change the order.
372
        // the second put() with the actual foreign row doesn't change the order.
332
        final ForeignCopyMode copyMode = ForeignCopyMode.COPY_NULL;
373
        final ForeignCopyMode copyMode = ForeignCopyMode.COPY_NULL;
333
        for (final SQLRowValues n : this.getItems()) {
374
        for (final SQLRowValues n : this.getItems()) {
334
            final SQLRowValues copy;
375
            final SQLRowValues copy;
-
 
376
            if (subset == null || subset.containsKey(n)) {
335
            // might as well use the minimum memory if the values won't change
377
                // might as well use the minimum memory if the values won't change
336
            if (freeze) {
378
                if (freeze) {
337
                copy = new SQLRowValues(n.getTable(), n.size(), n.getForeignsSize(), n.getReferents().size());
379
                    copy = new SQLRowValues(n.getTable(), n.size(), n.getForeignsSize(), n.getReferents().size());
338
                copy.setAll(n.getAllValues(copyMode));
380
                    copy.setAll(n.getAllValues(copyMode));
339
            } else {
381
                } else {
340
                copy = new SQLRowValues(n, copyMode);
382
                    copy = new SQLRowValues(n, copyMode);
-
 
383
                    if (subset != null) {
-
 
384
                        copy.retainAll(subset.get(n));
-
 
385
                    }
341
            }
386
                }
342
            noLinkCopy.put(n, copy);
387
                noLinkCopy.put(n, copy);
343
        }
388
            }
-
 
389
        }
344
 
390
 
345
        // and link them together in order
391
        // and link them together in order
-
 
392
        final List<Link> iterableLinks = subset == null ? this.links : new ArrayList<Link>(this.links);
346
        for (final Link l : this.links) {
393
        for (final Link l : iterableLinks) {
347
            if (l.getField() != null) {
394
            if (l.getField() != null) {
-
 
395
                final SQLRowValues sourceCopy = noLinkCopy.get(l.getSrc());
-
 
396
                if (subset == null || (sourceCopy != null && sourceCopy.contains(l.getField().getName()))) {
-
 
397
                    assert l.getDest() != null;
348
                noLinkCopy.get(l.getSrc()).put(l.getField().getName(), noLinkCopy.get(l.getDest()));
398
                    final SQLRowValues destCopy = noLinkCopy.get(l.getDest());
-
 
399
                    final Object dest;
-
 
400
                    if (destCopy != null)
-
 
401
                        dest = destCopy;
-
 
402
                    else if (allowSameGraph)
-
 
403
                        dest = l.getDest();
-
 
404
                    else if (l.getDest().hasID())
-
 
405
                        dest = l.getDest().getIDNumber();
-
 
406
                    else
-
 
407
                        dest = null;
-
 
408
                    if (dest != null) {
-
 
409
                        sourceCopy.put(l.getField().getName(), dest);
-
 
410
                    } else {
-
 
411
                        // ForeignCopyMode.COPY_ID_OR_RM like pruneWithoutCopy() (avoids
-
 
412
                        // leaving nulls)
-
 
413
                        sourceCopy.remove(l.getField().getName());
-
 
414
                    }
-
 
415
                }
349
            } else {
416
            } else {
350
                assert noLinkCopy.containsKey(l.getSrc());
417
                assert subset != null || noLinkCopy.containsKey(l.getSrc());
351
            }
418
            }
352
        }
419
        }
353
 
420
 
354
        final SQLRowValues res = noLinkCopy.values().iterator().next();
421
        final SQLRowValues res = noLinkCopy.values().iterator().next();
355
        // only force graph creation if needed
422
        // only force graph creation if needed
356
        if (freeze)
423
        if (freeze)
357
            res.getGraph().freeze();
424
            res.getGraph().freeze();
358
        assert res.isFrozen() == freeze;
425
        assert res.isFrozen() == freeze;
-
 
426
        assert allowSameGraph || res.getGraph() != this;
359
 
427
 
360
        return noLinkCopy;
428
        return noLinkCopy;
361
    }
429
    }
362
 
430
 
363
    public final StoreResult insert() throws SQLException {
431
    public final StoreResult insert() throws SQLException {
Line 894... Line 962...
894
     */
962
     */
895
    public final SQLRowValues prune(final SQLRowValues start, final SQLRowValues graph, final boolean keepUnionOfFields) {
963
    public final SQLRowValues prune(final SQLRowValues start, final SQLRowValues graph, final boolean keepUnionOfFields) {
896
        return pruneMap(start, graph, keepUnionOfFields).get(start);
964
        return pruneMap(start, graph, keepUnionOfFields).get(start);
897
    }
965
    }
898
 
966
 
899
    // private since result isn't trimmed, the values are still all there, not all in the pruned
-
 
900
    // graph
-
 
901
    private final Map<SQLRowValues, SQLRowValues> pruneMap(final SQLRowValues start, final SQLRowValues graph, final boolean keepUnionOfFields) {
967
    public final Map<SQLRowValues, SQLRowValues> pruneMap(final SQLRowValues start, final SQLRowValues graph, final boolean keepUnionOfFields) {
902
        this.containsCheck(start);
968
        this.containsCheck(start);
-
 
969
        return this.copy(computeToRetain(start, graph, keepUnionOfFields), false, false);
-
 
970
    }
-
 
971
 
-
 
972
    static SetMap<SQLRowValues, String> computeToRetain(final SQLRowValues start, final SQLRowValues graph, final boolean keepUnionOfFields) {
903
        if (!start.getTable().equals(graph.getTable()))
973
        if (!start.getTable().equals(graph.getTable()))
904
            throw new IllegalArgumentException(start + " is not from the same table as " + graph);
974
            throw new IllegalArgumentException(start + " is not from the same table as " + graph);
905
        // there's no way to tell apart 2 referents
975
        // there's no way to tell apart 2 referents
906
        if (!graph.getGraph().hasOneRowPerPath())
976
        if (!graph.getGraph().hasOneRowPerPath())
907
            throw new IllegalArgumentException("More than one row for " + graph.printGraph());
977
            throw new IllegalArgumentException("More than one row for " + graph.printGraph());
908
 
978
 
909
        final Map<SQLRowValues, SQLRowValues> map = start.getGraph().deepCopy(false);
-
 
910
        final SQLRowValues res = map.get(start);
-
 
911
 
-
 
912
        final SetMap<SQLRowValues, String> toRetain = new SetMap<SQLRowValues, String>(new IdentityHashMap<SQLRowValues, Set<String>>(), Mode.NULL_FORBIDDEN);
979
        final SetMap<SQLRowValues, String> toRetain = new SetMap<SQLRowValues, String>(new IdentityHashMap<SQLRowValues, Set<String>>(), Mode.NULL_FORBIDDEN);
913
        // BREADTH_FIRST to stop as soon as this no longer have rows in the graph
980
        // BREADTH_FIRST to stop as soon as this no longer have rows in the graph
914
        // CycleAllowed since we need to go through every path (e.g. what is a cycle in graph might
981
        // CycleAllowed since we need to go through every path (e.g. what is a cycle in graph might
915
        // not be in this :
982
        // not be in this :
916
        // SITE1 -> CONTACT1 -> SITE1 for graph and
983
        // SITE1 -> CONTACT1 -> SITE1 for graph and
Line 919... Line 986...
919
        graph.getGraph().walk(graph, null, new ITransformer<State<Object>, Object>() {
986
        graph.getGraph().walk(graph, null, new ITransformer<State<Object>, Object>() {
920
            @Override
987
            @Override
921
            public Object transformChecked(State<Object> input) {
988
            public Object transformChecked(State<Object> input) {
922
                final SQLRowValues r = input.getCurrent();
989
                final SQLRowValues r = input.getCurrent();
923
                // since we allowed cycles in graph, allow it here
990
                // since we allowed cycles in graph, allow it here
924
                final Collection<SQLRowValues> rows = res.followPath(input.getPath(), CreateMode.CREATE_NONE, false, true);
991
                final Collection<SQLRowValues> rows = start.followPath(input.getPath(), CreateMode.CREATE_NONE, false, true);
925
                // since we're using BREADTH_FIRST, the next path will be longer so no need to
992
                // since we're using BREADTH_FIRST, the next path will be longer so no need to
926
                // continue if there's already no row
993
                // continue if there's already no row
927
                if (rows.isEmpty())
994
                if (rows.isEmpty())
928
                    throw new StopRecurseException().setCompletely(false);
995
                    throw new StopRecurseException().setCompletely(false);
929
                for (final SQLRowValues row : rows) {
996
                for (final SQLRowValues row : rows) {
Line 933... Line 1000...
933
                        toRetain.getCollection(row).retainAll(r.getFields());
1000
                        toRetain.getCollection(row).retainAll(r.getFields());
934
                }
1001
                }
935
                return null;
1002
                return null;
936
            }
1003
            }
937
        }, walkOptions);
1004
        }, walkOptions);
-
 
1005
        return toRetain;
-
 
1006
    }
-
 
1007
 
-
 
1008
    public final SQLRowValues pruneWithoutCopy(final SQLRowValues start, final SQLRowValues graph) {
-
 
1009
        return this.pruneWithoutCopy(start, graph, true);
-
 
1010
    }
-
 
1011
 
-
 
1012
    public final SQLRowValues pruneWithoutCopy(final SQLRowValues start, final SQLRowValues graph, final boolean keepUnionOfFields) {
-
 
1013
        this.containsCheck(start);
-
 
1014
        final SetMap<SQLRowValues, String> toRetain = computeToRetain(start, graph, keepUnionOfFields);
-
 
1015
        assert toRetain.containsKey(start);
938
 
1016
 
939
        // remove extra fields and flatten if necessary
1017
        // remove extra fields and flatten if necessary
940
        for (final Entry<SQLRowValues, Set<String>> e : toRetain.entrySet()) {
1018
        for (final Entry<SQLRowValues, Set<String>> e : toRetain.entrySet()) {
941
            final SQLRowValues r = e.getKey();
1019
            final SQLRowValues r = e.getKey();
942
            r.retainAll(e.getValue());
1020
            r.retainAll(e.getValue());
Line 950... Line 1028...
950
            for (final String fieldToFlatten : toFlatten) {
1028
            for (final String fieldToFlatten : toFlatten) {
951
                r.flatten(fieldToFlatten, ForeignCopyMode.COPY_ID_OR_RM);
1029
                r.flatten(fieldToFlatten, ForeignCopyMode.COPY_ID_OR_RM);
952
            }
1030
            }
953
        }
1031
        }
954
        // now, remove referents that aren't in the graph
1032
        // now, remove referents that aren't in the graph
955
        for (final SQLRowValues r : new ArrayList<SQLRowValues>(res.getGraph().getItems())) {
1033
        for (final SQLRowValues r : new ArrayList<SQLRowValues>(start.getGraph().getItems())) {
956
            if (!toRetain.containsKey(r)) {
1034
            if (!toRetain.containsKey(r)) {
957
                // only remove links at the border and even then, only remove links to the result :
1035
                // only remove links at the border and even then, only remove links to the result :
958
                // avoid creating a myriad of graphs
1036
                // avoid creating a myriad of graphs
959
                final Set<String> toFlatten = new HashSet<String>();
1037
                final Set<String> toFlatten = new HashSet<String>();
960
                for (final Entry<String, SQLRowValues> e2 : r.getForeigns().entrySet()) {
1038
                for (final Entry<String, SQLRowValues> e2 : r.getForeigns().entrySet()) {
Line 964... Line 1042...
964
                    }
1042
                    }
965
                }
1043
                }
966
                r.removeAll(toFlatten);
1044
                r.removeAll(toFlatten);
967
            }
1045
            }
968
        }
1046
        }
969
        assert res.getGraph().getItems().equals(toRetain.keySet());
1047
        assert start.getGraph().getItems().equals(toRetain.keySet());
970
 
-
 
-
 
1048
        // NOTE this instance no longer the graph of start if referents were removed
971
        return map;
1049
        return start;
972
    }
1050
    }
973
 
1051
 
974
    // TODO handle referents (and decide how to handle multiple paths to the same node)
1052
    // TODO handle referents (and decide how to handle multiple paths to the same node)
975
    final void grow(final SQLRowValues start, final SQLRowValues toGrow, final boolean checkFields) {
1053
    final void grow(final SQLRowValues start, final SQLRowValues toGrow, final boolean checkFields, final boolean growUndefined) {
976
        this.containsCheck(start);
1054
        this.containsCheck(start);
977
        if (!start.getTable().equals(toGrow.getTable()))
1055
        if (!start.getTable().equals(toGrow.getTable()))
978
            throw new IllegalArgumentException(start + " is not from the same table as " + toGrow);
1056
            throw new IllegalArgumentException(start + " is not from the same table as " + toGrow);
979
        this.walk(start, null, new ITransformer<State<Object>, Object>() {
1057
        this.walk(start, toGrow, new ITransformer<State<SQLRowValues>, SQLRowValues>() {
980
            @Override
1058
            @Override
981
            public Object transformChecked(State<Object> input) {
1059
            public SQLRowValues transformChecked(State<SQLRowValues> input) {
982
                final SQLRowValues existing = toGrow.followPath(input.getPath());
1060
                final SQLRowValues existing = toGrow.followPath(input.getPath());
-
 
1061
                // don't add undefined row if there's none or if don't want
-
 
1062
                if (existing == null && input.getAcc().isForeignEmpty(input.getFrom().getName()) && (input.getPath().getLast().getUndefinedIDNumber() == null || !growUndefined))
-
 
1063
                    throw new StopRecurseException().setCompletely(false);
983
                if (existing == null || (checkFields && !existing.getFields().containsAll(input.getCurrent().getFields()))) {
1064
                if (existing == null || (checkFields && !existing.getFields().containsAll(input.getCurrent().getFields()))) {
984
                    final SQLRowValues leaf = toGrow.assurePath(input.getPath());
1065
                    final SQLRowValues leaf = toGrow.assurePath(input.getPath());
985
                    if (leaf.hasID()) {
1066
                    if (leaf.hasID()) {
986
                        final SQLRowValuesListFetcher fetcher = new SQLRowValuesListFetcher(input.getCurrent());
1067
                        final SQLRowValuesListFetcher fetcher = new SQLRowValuesListFetcher(input.getCurrent());
-
 
1068
                        // don't exclude undef otherwise cannot grow eg
-
 
1069
                        // LOCAL.ID_FAMILLE_2 = 1
-
 
1070
                        if (growUndefined) {
987
                        fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
1071
                            fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
988
                            @Override
1072
                                @Override
989
                            public SQLSelect transformChecked(SQLSelect input) {
1073
                                public SQLSelect transformChecked(SQLSelect input) {
990
                                // don't exclude undef otherwise cannot grow eg
-
 
991
                                // LOCAL.ID_FAMILLE_2 = 1
-
 
992
                                input.setExcludeUndefined(false);
1074
                                    input.setExcludeUndefined(false);
993
                                return input;
1075
                                    return input;
994
                            }
1076
                                }
995
                        });
1077
                            });
-
 
1078
                        }
996
                        final SQLRowValues fetched = fetcher.fetchOne(leaf.getIDNumber());
1079
                        final SQLRowValues fetched = fetcher.fetchOne(leaf.getIDNumber());
997
                        if (fetched == null)
1080
                        if (fetched == null)
998
                            throw new IllegalArgumentException("no row for " + fetcher);
1081
                            throw new IllegalArgumentException("no row for " + fetcher);
999
                        leaf.load(fetched, null);
1082
                        leaf.load(fetched, null);
-
 
1083
                        // we already loaded all further rows
-
 
1084
                        throw new StopRecurseException().setCompletely(false);
1000
                    } else
1085
                    } else {
1001
                        throw new IllegalArgumentException("cannot expand, missing ID in " + leaf + " at " + input.getPath());
1086
                        throw new IllegalArgumentException("cannot expand, missing ID in " + leaf + " at " + input.getPath());
1002
                }
1087
                    }
-
 
1088
                } else {
1003
                return null;
1089
                    return existing;
-
 
1090
                }
1004
            }
1091
            }
1005
        }, RecursionType.BREADTH_FIRST);
1092
        }, RecursionType.BREADTH_FIRST, Direction.FOREIGN);
1006
    }
1093
    }
1007
 
1094
 
1008
    public final String contains(final SQLRowValues start, SQLRowValues graph) {
1095
    public final String contains(final SQLRowValues start, SQLRowValues graph) {
1009
        return this.contains(start, graph, true);
1096
        return this.contains(start, graph, true);
1010
    }
1097
    }