Make CopyHistory implement CopyableNode
[yangtools.git] / yang / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / CopyHistory.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.spi.meta;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.Verify;
14 import java.util.Arrays;
15 import java.util.stream.Collectors;
16 import org.opendaylight.yangtools.concepts.Immutable;
17 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
18
19 @Beta
20 public final class CopyHistory implements Immutable, CopyableNode {
21     private static final CopyType[] VALUES = CopyType.values();
22
23     private static final CopyHistory[][] CACHE = new CopyHistory[VALUES.length][];
24
25     static {
26         /*
27          * Cache size is dependent on number of items in CopyType, it costs N * 2^N objects.
28          * For 4 types that boils down to 4 * 16 = 64 objects.
29          * For 5 types that boils down to 5 * 32 = 160 objects.
30          * For 6 types that boils down to 6 * 64 = 384 objects.
31          *
32          * If we ever hit 6 types, the caching strategy needs to be revisited.
33          */
34         Verify.verify(VALUES.length < 6);
35     }
36
37     private static final CopyHistory ORIGINAL = cacheObject(CopyType.ORIGINAL, CopyType.ORIGINAL.bit());
38     private static final int IS_ADDED_BY_USES_BITS =
39         CopyType.ADDED_BY_USES_AUGMENTATION.bit() | CopyType.ADDED_BY_USES.bit();
40     private static final int IS_AUGMENTING_BITS =
41         CopyType.ADDED_BY_USES_AUGMENTATION.bit() | CopyType.ADDED_BY_AUGMENTATION.bit();
42
43     private final short operations;
44     private final short lastOperation;
45
46     private CopyHistory(final int operations, final CopyType lastOperation) {
47         this.operations = (short) operations;
48         this.lastOperation = (short) lastOperation.ordinal();
49     }
50
51     public static CopyHistory original() {
52         return ORIGINAL;
53     }
54
55     public static CopyHistory of(final CopyType copyType, final CopyHistory copyHistory) {
56         return ORIGINAL.append(copyType, copyHistory);
57     }
58
59     private static CopyHistory[] cacheArray(final CopyType lastOperation) {
60         final int ordinal = lastOperation.ordinal();
61         CopyHistory[] ret = CACHE[ordinal];
62         if (ret == null) {
63             synchronized (CACHE) {
64                 ret = CACHE[ordinal];
65                 if (ret == null) {
66                     ret = new CopyHistory[1 << VALUES.length];
67                     CACHE[ordinal] = ret;
68                 }
69             }
70         }
71
72         return ret;
73     }
74
75     private static CopyHistory cacheObject(final CopyType lastOperation, final int operations) {
76         final CopyHistory[] array = cacheArray(lastOperation);
77         CopyHistory ret = array[operations];
78         if (ret == null) {
79             synchronized (array) {
80                 ret = array[operations];
81                 if (ret == null) {
82                     ret = new CopyHistory(operations, lastOperation);
83                     array[operations] = ret;
84                 }
85             }
86         }
87
88         return ret;
89     }
90
91     @VisibleForTesting
92     // FIXME: 7.0.0: hide this method
93     public boolean contains(final CopyType type) {
94         return (operations & type.bit()) != 0;
95     }
96
97     public CopyType getLastOperation() {
98         return VALUES[lastOperation];
99     }
100
101     @Override
102     @Deprecated
103     public boolean isAugmenting() {
104         return (operations & IS_AUGMENTING_BITS) != 0;
105     }
106
107     @Override
108     @Deprecated
109     public boolean isAddedByUses() {
110         return (operations & IS_ADDED_BY_USES_BITS) != 0;
111     }
112
113     @VisibleForTesting
114     CopyHistory append(final CopyType typeOfCopy, final CopyHistory toAppend) {
115         final int newOperations = operations | toAppend.operations | typeOfCopy.bit();
116         if (newOperations == operations && typeOfCopy.ordinal() == lastOperation) {
117             return this;
118         }
119
120         return cacheObject(typeOfCopy, newOperations);
121     }
122
123     @Override
124     public int hashCode() {
125         return Integer.hashCode(operations | lastOperation << Short.SIZE);
126     }
127
128     @Override
129     public boolean equals(final Object obj) {
130         if (obj == this) {
131             return true;
132         }
133         if (!(obj instanceof CopyHistory)) {
134             return false;
135         }
136         final CopyHistory other = (CopyHistory) obj;
137         return operations == other.operations && lastOperation == other.lastOperation;
138     }
139
140     @Override
141     public String toString() {
142         return MoreObjects.toStringHelper(this).add("lastOperation", getLastOperation())
143                 .add("operations", Arrays.stream(VALUES).filter(value -> (value.bit() & operations) != 0)
144                     .collect(Collectors.toList()))
145                 .toString();
146     }
147 }