22
33import java .util .ArrayList ;
44import java .util .Collections ;
5+ import java .util .HashMap ;
56import java .util .IdentityHashMap ;
67import java .util .LinkedHashMap ;
78import java .util .List ;
1011
1112import ai .timefold .solver .core .config .util .ConfigUtils ;
1213import ai .timefold .solver .core .impl .domain .valuerange .descriptor .FromEntityPropertyValueRangeDescriptor ;
14+ import ai .timefold .solver .core .impl .domain .valuerange .sort .ValueRangeSorter ;
15+ import ai .timefold .solver .core .impl .heuristic .selector .common .decorator .SelectionSorter ;
1316
1417import org .jspecify .annotations .NullMarked ;
1518import org .jspecify .annotations .Nullable ;
2124 * @see FromEntityPropertyValueRangeDescriptor
2225 */
2326@ NullMarked
24- public final class ReachableValues {
27+ public final class ReachableValues < E , V > {
2528
26- private final Map <Object , ReachableItemValue > values ;
29+ private final Map <V , ReachableItemValue < E , V > > values ;
2730 private final @ Nullable Class <?> valueClass ;
31+ private final @ Nullable ValueRangeSorter <V > valueRangeSorter ;
2832 private final boolean acceptsNullValue ;
29- private @ Nullable ReachableItemValue firstCachedObject ;
30- private @ Nullable ReachableItemValue secondCachedObject ;
33+ private @ Nullable ReachableItemValue < E , V > firstCachedObject ;
34+ private @ Nullable ReachableItemValue < E , V > secondCachedObject ;
3135
32- public ReachableValues (Map <Object , ReachableItemValue > values , Class <?> valueClass , boolean acceptsNullValue ) {
36+ public ReachableValues (Map <V , ReachableItemValue <E , V >> values , @ Nullable Class <?> valueClass ,
37+ @ Nullable ValueRangeSorter <V > valueRangeSorter , boolean acceptsNullValue ) {
3338 this .values = values ;
3439 this .valueClass = valueClass ;
40+ this .valueRangeSorter = valueRangeSorter ;
3541 this .acceptsNullValue = acceptsNullValue ;
3642 }
3743
38- private @ Nullable ReachableItemValue fetchItemValue (Object value ) {
39- ReachableItemValue selected = null ;
44+ private @ Nullable ReachableItemValue < E , V > fetchItemValue (V value ) {
45+ ReachableItemValue < E , V > selected = null ;
4046 if (firstCachedObject != null && firstCachedObject .value == value ) {
4147 selected = firstCachedObject ;
4248 } else if (secondCachedObject != null && secondCachedObject .value == value ) {
@@ -54,27 +60,28 @@ public ReachableValues(Map<Object, ReachableItemValue> values, Class<?> valueCla
5460 return selected ;
5561 }
5662
57- public List <Object > extractEntitiesAsList (Object value ) {
63+ public List <E > extractEntitiesAsList (V value ) {
5864 var itemValue = fetchItemValue (value );
5965 if (itemValue == null ) {
6066 return Collections .emptyList ();
6167 }
6268 return itemValue .randomAccessEntityList ;
6369 }
6470
65- public List <Object > extractValuesAsList (Object value ) {
71+ public List <V > extractValuesAsList (V value ) {
6672 var itemValue = fetchItemValue (value );
6773 if (itemValue == null ) {
6874 return Collections .emptyList ();
6975 }
76+ itemValue .checkSorting (valueRangeSorter );
7077 return itemValue .randomAccessValueList ;
7178 }
7279
7380 public int getSize () {
7481 return values .size ();
7582 }
7683
77- public boolean isEntityReachable (@ Nullable Object origin , @ Nullable Object entity ) {
84+ public boolean isEntityReachable (@ Nullable V origin , @ Nullable E entity ) {
7885 if (entity == null ) {
7986 return true ;
8087 }
@@ -88,7 +95,7 @@ public boolean isEntityReachable(@Nullable Object origin, @Nullable Object entit
8895 return originItemValue .entityMap .containsKey (entity );
8996 }
9097
91- public boolean isValueReachable (Object origin , @ Nullable Object otherValue ) {
98+ public boolean isValueReachable (V origin , @ Nullable V otherValue ) {
9299 var originItemValue = fetchItemValue (Objects .requireNonNull (origin ));
93100 if (originItemValue == null ) {
94101 return false ;
@@ -103,19 +110,34 @@ public boolean acceptsNullValue() {
103110 return acceptsNullValue ;
104111 }
105112
106- public boolean matchesValueClass (Object value ) {
113+ public boolean matchesValueClass (V value ) {
107114 return valueClass != null && valueClass .isAssignableFrom (Objects .requireNonNull (value ).getClass ());
108115 }
109116
117+ public @ Nullable SelectionSorter <?, V > getValueSelectionSorter () {
118+ return valueRangeSorter != null ? valueRangeSorter .getInnerSorter () : null ;
119+ }
120+
121+ public ReachableValues <E , V > copy (@ Nullable ValueRangeSorter <V > valueRangeSorter ) {
122+ Map <V , ReachableItemValue <E , V >> newValues = ConfigUtils .isGenericTypeImmutable (valueClass )
123+ ? new HashMap <>(values .size ())
124+ : new IdentityHashMap <>(values .size ());
125+ for (Map .Entry <V , ReachableItemValue <E , V >> entry : values .entrySet ()) {
126+ newValues .put (entry .getKey (), entry .getValue ().copy ());
127+ }
128+ return new ReachableValues <>(newValues , valueClass , valueRangeSorter , acceptsNullValue );
129+ }
130+
110131 @ NullMarked
111- public static final class ReachableItemValue {
112- private final Object value ;
113- private final Map <Object , Object > entityMap ;
114- private final Map <Object , Object > valueMap ;
115- private final List <Object > randomAccessEntityList ;
116- private final List <Object > randomAccessValueList ;
117-
118- public ReachableItemValue (Object value , int entityListSize , int valueListSize ) {
132+ public static final class ReachableItemValue <E , V > {
133+ private final V value ;
134+ private final Map <E , E > entityMap ;
135+ private final Map <V , V > valueMap ;
136+ private final List <E > randomAccessEntityList ;
137+ private final List <V > randomAccessValueList ;
138+ private boolean sorted = false ;
139+
140+ public ReachableItemValue (V value , int entityListSize , int valueListSize ) {
119141 this .value = value ;
120142 this .entityMap = new IdentityHashMap <>(entityListSize );
121143 this .randomAccessEntityList = new ArrayList <>(entityListSize );
@@ -124,17 +146,40 @@ public ReachableItemValue(Object value, int entityListSize, int valueListSize) {
124146 this .randomAccessValueList = new ArrayList <>(valueListSize );
125147 }
126148
127- public void addEntity (Object entity ) {
149+ private ReachableItemValue (V value , Map <E , E > entityMap , Map <V , V > valueMap , List <E > randomAccessEntityList ,
150+ List <V > randomAccessValueList ) {
151+ this .value = value ;
152+ this .entityMap = entityMap ;
153+ this .valueMap = valueMap ;
154+ this .randomAccessEntityList = randomAccessEntityList ;
155+ this .randomAccessValueList = randomAccessValueList ;
156+ }
157+
158+ public void addEntity (E entity ) {
128159 if (entityMap .put (entity , entity ) == null ) {
129160 randomAccessEntityList .add (entity );
130161 }
131162 }
132163
133- public void addValue (Object value ) {
164+ public void addValue (V value ) {
134165 if (valueMap .put (value , value ) == null ) {
135166 randomAccessValueList .add (value );
136167 }
137168 }
169+
170+ private void checkSorting (@ Nullable ValueRangeSorter <V > valueRangeSorter ) {
171+ if (valueRangeSorter != null && !sorted ) {
172+ var sortedList = valueRangeSorter .sort (randomAccessValueList );
173+ randomAccessValueList .clear ();
174+ randomAccessValueList .addAll (sortedList );
175+ sorted = true ;
176+ }
177+ }
178+
179+ public ReachableItemValue <E , V > copy () {
180+ return new ReachableItemValue <>(value , entityMap , valueMap , new ArrayList <>(randomAccessEntityList ),
181+ new ArrayList <>(randomAccessValueList ));
182+ }
138183 }
139184
140185}
0 commit comments