View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.cache;
17  
18  import java.io.Serializable;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.StringJoiner;
22  
23  import org.apache.ibatis.reflection.ArrayUtil;
24  
25  /**
26   * @author Clinton Begin
27   */
28  public class CacheKey implements Cloneable, Serializable {
29  
30    private static final long serialVersionUID = 1146682552656046210L;
31  
32    public static final CacheKey NULL_CACHE_KEY = new CacheKey() {
33  
34      @Override
35      public void update(Object object) {
36        throw new CacheException("Not allowed to update a null cache key instance.");
37      }
38  
39      @Override
40      public void updateAll(Object[] objects) {
41        throw new CacheException("Not allowed to update a null cache key instance.");
42      }
43    };
44  
45    private static final int DEFAULT_MULTIPLIER = 37;
46    private static final int DEFAULT_HASHCODE = 17;
47  
48    private final int multiplier;
49    private int hashcode;
50    private long checksum;
51    private int count;
52    // 8/21/2017 - Sonarlint flags this as needing to be marked transient. While true if content is not serializable, this
53    // is not always true and thus should not be marked transient.
54    private List<Object> updateList;
55  
56    public CacheKey() {
57      this.hashcode = DEFAULT_HASHCODE;
58      this.multiplier = DEFAULT_MULTIPLIER;
59      this.count = 0;
60      this.updateList = new ArrayList<>();
61    }
62  
63    public CacheKey(Object[] objects) {
64      this();
65      updateAll(objects);
66    }
67  
68    public int getUpdateCount() {
69      return updateList.size();
70    }
71  
72    public void update(Object object) {
73      int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
74  
75      count++;
76      checksum += baseHashCode;
77      baseHashCode *= count;
78  
79      hashcode = multiplier * hashcode + baseHashCode;
80  
81      updateList.add(object);
82    }
83  
84    public void updateAll(Object[] objects) {
85      for (Object o : objects) {
86        update(o);
87      }
88    }
89  
90    @Override
91    public boolean equals(Object object) {
92      if (this == object) {
93        return true;
94      }
95      if (!(object instanceof CacheKey)) {
96        return false;
97      }
98  
99      final CacheKey cacheKey = (CacheKey) object;
100 
101     if (hashcode != cacheKey.hashcode) {
102       return false;
103     }
104     if (checksum != cacheKey.checksum) {
105       return false;
106     }
107     if (count != cacheKey.count) {
108       return false;
109     }
110 
111     for (int i = 0; i < updateList.size(); i++) {
112       Object thisObject = updateList.get(i);
113       Object thatObject = cacheKey.updateList.get(i);
114       if (!ArrayUtil.equals(thisObject, thatObject)) {
115         return false;
116       }
117     }
118     return true;
119   }
120 
121   @Override
122   public int hashCode() {
123     return hashcode;
124   }
125 
126   @Override
127   public String toString() {
128     StringJoiner returnValue = new StringJoiner(":");
129     returnValue.add(String.valueOf(hashcode));
130     returnValue.add(String.valueOf(checksum));
131     updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
132     return returnValue.toString();
133   }
134 
135   @Override
136   public CacheKey clone() throws CloneNotSupportedException {
137     CacheKey clonedCacheKey = (CacheKey) super.clone();
138     clonedCacheKey.updateList = new ArrayList<>(updateList);
139     return clonedCacheKey;
140   }
141 
142 }