diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index b070c03fdce..2d9c04350dd 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -202,6 +202,12 @@ API Changes which takes Analyzer as a parameter, for easier customization by subclasses. (Robert Muir) +* LUCENE-2953: PriorityQueue's internal heap was made private, as subclassing + with generics can lead to ClassCastException. For advanced use (e.g. in Solr) + a method getHeapArray() was added to retrieve the internal heap array as a + non-generic Object[]. Also the initialize(int) function was moved into the + ctor. (Uwe Schindler, Yonik Seeley) + New features * LUCENE-2604: Added RegexpQuery support to QueryParser. Regular expressions diff --git a/lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/Highlighter.java b/lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/Highlighter.java index 1a692d62c84..5deafd62faa 100644 --- a/lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/Highlighter.java +++ b/lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/Highlighter.java @@ -524,7 +524,7 @@ class FragmentQueue extends PriorityQueue { public FragmentQueue(int size) { - initialize(size); + super(size); } @Override diff --git a/lucene/contrib/misc/src/java/org/apache/lucene/misc/HighFreqTerms.java b/lucene/contrib/misc/src/java/org/apache/lucene/misc/HighFreqTerms.java index 77d29820660..cc9ce2635f7 100644 --- a/lucene/contrib/misc/src/java/org/apache/lucene/misc/HighFreqTerms.java +++ b/lucene/contrib/misc/src/java/org/apache/lucene/misc/HighFreqTerms.java @@ -255,7 +255,7 @@ final class TotalTermFreqComparatorSortDescending implements Comparator { TermStatsQueue(int size) { - initialize(size); + super(size); } @Override diff --git a/lucene/contrib/queries/src/java/org/apache/lucene/search/FuzzyLikeThisQuery.java b/lucene/contrib/queries/src/java/org/apache/lucene/search/FuzzyLikeThisQuery.java index 13ec241f64c..0e42f56e9cd 100644 --- a/lucene/contrib/queries/src/java/org/apache/lucene/search/FuzzyLikeThisQuery.java +++ b/lucene/contrib/queries/src/java/org/apache/lucene/search/FuzzyLikeThisQuery.java @@ -335,7 +335,7 @@ public class FuzzyLikeThisQuery extends Query private static class ScoreTermQueue extends PriorityQueue { public ScoreTermQueue(int size){ - initialize(size); + super(size); } /* (non-Javadoc) diff --git a/lucene/contrib/queries/src/java/org/apache/lucene/search/similar/MoreLikeThis.java b/lucene/contrib/queries/src/java/org/apache/lucene/search/similar/MoreLikeThis.java index 2b9b429c47a..3a944197752 100644 --- a/lucene/contrib/queries/src/java/org/apache/lucene/search/similar/MoreLikeThis.java +++ b/lucene/contrib/queries/src/java/org/apache/lucene/search/similar/MoreLikeThis.java @@ -1006,7 +1006,7 @@ public final class MoreLikeThis { */ private static class FreqQ extends PriorityQueue { FreqQ (int s) { - initialize(s); + super(s); } @Override diff --git a/lucene/contrib/spellchecker/src/java/org/apache/lucene/search/spell/SuggestWordQueue.java b/lucene/contrib/spellchecker/src/java/org/apache/lucene/search/spell/SuggestWordQueue.java index af3f9d0e055..7d8de51f34d 100755 --- a/lucene/contrib/spellchecker/src/java/org/apache/lucene/search/spell/SuggestWordQueue.java +++ b/lucene/contrib/spellchecker/src/java/org/apache/lucene/search/spell/SuggestWordQueue.java @@ -41,7 +41,7 @@ public final class SuggestWordQueue extends PriorityQueue { * @param size The size of the queue */ public SuggestWordQueue (int size) { - initialize(size); + super(size); comparator = DEFAULT_COMPARATOR; } @@ -51,7 +51,7 @@ public final class SuggestWordQueue extends PriorityQueue { * @param comparator The comparator. */ public SuggestWordQueue(int size, Comparator comparator){ - initialize(size); + super(size); this.comparator = comparator; } diff --git a/lucene/src/java/org/apache/lucene/index/MultiFieldsEnum.java b/lucene/src/java/org/apache/lucene/index/MultiFieldsEnum.java index bd9856d9297..06ecd3bb5db 100644 --- a/lucene/src/java/org/apache/lucene/index/MultiFieldsEnum.java +++ b/lucene/src/java/org/apache/lucene/index/MultiFieldsEnum.java @@ -129,7 +129,7 @@ public final class MultiFieldsEnum extends FieldsEnum { private final static class FieldMergeQueue extends PriorityQueue { FieldMergeQueue(int size) { - initialize(size); + super(size); } @Override diff --git a/lucene/src/java/org/apache/lucene/index/MultiTermsEnum.java b/lucene/src/java/org/apache/lucene/index/MultiTermsEnum.java index f3283939e04..67ba8883da2 100644 --- a/lucene/src/java/org/apache/lucene/index/MultiTermsEnum.java +++ b/lucene/src/java/org/apache/lucene/index/MultiTermsEnum.java @@ -427,7 +427,7 @@ public final class MultiTermsEnum extends TermsEnum { private final static class TermMergeQueue extends PriorityQueue { Comparator termComp; TermMergeQueue(int size) { - initialize(size); + super(size); } @Override diff --git a/lucene/src/java/org/apache/lucene/search/FieldValueHitQueue.java b/lucene/src/java/org/apache/lucene/search/FieldValueHitQueue.java index 394d13539ed..601a662ac98 100644 --- a/lucene/src/java/org/apache/lucene/search/FieldValueHitQueue.java +++ b/lucene/src/java/org/apache/lucene/search/FieldValueHitQueue.java @@ -56,15 +56,13 @@ public abstract class FieldValueHitQueue extends PriorityQueue 0, therefore no // need to check it again. diff --git a/lucene/src/java/org/apache/lucene/search/HitQueue.java b/lucene/src/java/org/apache/lucene/search/HitQueue.java index 7350f3eec1c..15e2052568c 100644 --- a/lucene/src/java/org/apache/lucene/search/HitQueue.java +++ b/lucene/src/java/org/apache/lucene/search/HitQueue.java @@ -63,17 +63,15 @@ final class HitQueue extends PriorityQueue { * @see #getSentinelObject() */ HitQueue(int size, boolean prePopulate) { - this.prePopulate = prePopulate; - initialize(size); + super(size, prePopulate); } - // Returns null if prePopulate is false. @Override protected ScoreDoc getSentinelObject() { // Always set the doc Id to MAX_VALUE so that it won't be favored by // lessThan. This generally should not happen since if score is not NEG_INF, // TopScoreDocCollector will always add the object to the queue. - return !prePopulate ? null : new ScoreDoc(Integer.MAX_VALUE, Float.NEGATIVE_INFINITY); + return new ScoreDoc(Integer.MAX_VALUE, Float.NEGATIVE_INFINITY); } @Override diff --git a/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java index c5c979cb904..7cb6994ccaa 100644 --- a/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java +++ b/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java @@ -432,7 +432,7 @@ class UnionDocsAndPositionsEnum extends DocsAndPositionsEnum { private static final class DocsQueue extends PriorityQueue { DocsQueue(List docsEnums) throws IOException { - initialize(docsEnums.size()); + super(docsEnums.size()); Iterator i = docsEnums.iterator(); while (i.hasNext()) { diff --git a/lucene/src/java/org/apache/lucene/search/PhraseQueue.java b/lucene/src/java/org/apache/lucene/search/PhraseQueue.java index 4b3496a3ba1..5b19567c59c 100644 --- a/lucene/src/java/org/apache/lucene/search/PhraseQueue.java +++ b/lucene/src/java/org/apache/lucene/search/PhraseQueue.java @@ -21,7 +21,7 @@ import org.apache.lucene.util.PriorityQueue; final class PhraseQueue extends PriorityQueue { PhraseQueue(int size) { - initialize(size); + super(size); } @Override diff --git a/lucene/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java b/lucene/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java index d92740a25c6..a81f930eba5 100644 --- a/lucene/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java +++ b/lucene/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java @@ -53,7 +53,7 @@ public class NearSpansUnordered extends Spans { private class CellQueue extends PriorityQueue { public CellQueue(int size) { - initialize(size); + super(size); } @Override diff --git a/lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java b/lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java index 2aeeb6dfe1d..68f52302cf6 100644 --- a/lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java +++ b/lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java @@ -145,7 +145,7 @@ public class SpanOrQuery extends SpanQuery implements Cloneable { private class SpanQueue extends PriorityQueue { public SpanQueue(int size) { - initialize(size); + super(size); } @Override diff --git a/lucene/src/java/org/apache/lucene/util/PriorityQueue.java b/lucene/src/java/org/apache/lucene/util/PriorityQueue.java index 27772052d9e..eebc7996256 100644 --- a/lucene/src/java/org/apache/lucene/util/PriorityQueue.java +++ b/lucene/src/java/org/apache/lucene/util/PriorityQueue.java @@ -28,8 +28,52 @@ package org.apache.lucene.util; */ public abstract class PriorityQueue { private int size; - private int maxSize; - protected T[] heap; + private final int maxSize; + private final T[] heap; + + public PriorityQueue(int maxSize) { + this(maxSize, true); + } + + @SuppressWarnings("unchecked") + public PriorityQueue(int maxSize, boolean prepopulate) { + size = 0; + int heapSize; + if (0 == maxSize) + // We allocate 1 extra to avoid if statement in top() + heapSize = 2; + else { + if (maxSize == Integer.MAX_VALUE) { + // Don't wrap heapSize to -1, in this case, which + // causes a confusing NegativeArraySizeException. + // Note that very likely this will simply then hit + // an OOME, but at least that's more indicative to + // caller that this values is too big. We don't +1 + // in this case, but it's very unlikely in practice + // one will actually insert this many objects into + // the PQ: + heapSize = Integer.MAX_VALUE; + } else { + // NOTE: we add +1 because all access to heap is + // 1-based not 0-based. heap[0] is unused. + heapSize = maxSize + 1; + } + } + heap = (T[]) new Object[heapSize]; // T is unbounded type, so this unchecked cast works always + this.maxSize = maxSize; + + if (prepopulate) { + // If sentinel objects are supported, populate the queue with them + T sentinel = getSentinelObject(); + if (sentinel != null) { + heap[1] = sentinel; + for (int i = 2; i < heap.length; i++) { + heap[i] = getSentinelObject(); + } + size = maxSize; + } + } + } /** Determines the ordering of objects in this priority queue. Subclasses * must define this one method. @@ -80,45 +124,6 @@ public abstract class PriorityQueue { return null; } - /** Subclass constructors must call this. */ - @SuppressWarnings("unchecked") - protected final void initialize(int maxSize) { - size = 0; - int heapSize; - if (0 == maxSize) - // We allocate 1 extra to avoid if statement in top() - heapSize = 2; - else { - if (maxSize == Integer.MAX_VALUE) { - // Don't wrap heapSize to -1, in this case, which - // causes a confusing NegativeArraySizeException. - // Note that very likely this will simply then hit - // an OOME, but at least that's more indicative to - // caller that this values is too big. We don't +1 - // in this case, but it's very unlikely in practice - // one will actually insert this many objects into - // the PQ: - heapSize = Integer.MAX_VALUE; - } else { - // NOTE: we add +1 because all access to heap is - // 1-based not 0-based. heap[0] is unused. - heapSize = maxSize + 1; - } - } - heap = (T[]) new Object[heapSize]; // T is unbounded type, so this unchecked cast works always - this.maxSize = maxSize; - - // If sentinel objects are supported, populate the queue with them - T sentinel = getSentinelObject(); - if (sentinel != null) { - heap[1] = sentinel; - for (int i = 2; i < heap.length; i++) { - heap[i] = getSentinelObject(); - } - size = maxSize; - } - } - /** * Adds an Object to a PriorityQueue in log(size) time. If one tries to add * more objects than maxSize from initialize an @@ -247,4 +252,11 @@ public abstract class PriorityQueue { } heap[i] = node; // install saved node } + + /** This method returns the internal heap array as Object[]. + * @lucene.internal + */ + protected final Object[] getHeapArray() { + return (Object[]) heap; + } } diff --git a/lucene/src/test/org/apache/lucene/util/TestPriorityQueue.java b/lucene/src/test/org/apache/lucene/util/TestPriorityQueue.java index 52f704e0e58..40bfbd7ca0c 100644 --- a/lucene/src/test/org/apache/lucene/util/TestPriorityQueue.java +++ b/lucene/src/test/org/apache/lucene/util/TestPriorityQueue.java @@ -23,8 +23,7 @@ public class TestPriorityQueue extends LuceneTestCase { private static class IntegerQueue extends PriorityQueue { public IntegerQueue(int count) { - super(); - initialize(count); + super(count); } @Override diff --git a/modules/benchmark/src/java/org/apache/lucene/benchmark/quality/utils/QualityQueriesFinder.java b/modules/benchmark/src/java/org/apache/lucene/benchmark/quality/utils/QualityQueriesFinder.java index 228b7fbb057..e9206ed353e 100755 --- a/modules/benchmark/src/java/org/apache/lucene/benchmark/quality/utils/QualityQueriesFinder.java +++ b/modules/benchmark/src/java/org/apache/lucene/benchmark/quality/utils/QualityQueriesFinder.java @@ -124,7 +124,7 @@ public class QualityQueriesFinder { private static class TermsDfQueue extends PriorityQueue { TermsDfQueue (int maxSize) { - initialize(maxSize); + super(maxSize); } @Override protected boolean lessThan(TermDf tf1, TermDf tf2) { diff --git a/solr/src/common/org/apache/solr/common/util/ConcurrentLRUCache.java b/solr/src/common/org/apache/solr/common/util/ConcurrentLRUCache.java index 33d76241d82..e3de94f7396 100644 --- a/solr/src/common/org/apache/solr/common/util/ConcurrentLRUCache.java +++ b/solr/src/common/org/apache/solr/common/util/ConcurrentLRUCache.java @@ -354,13 +354,17 @@ public class ConcurrentLRUCache { private static class PQueue extends PriorityQueue> { int myMaxSize; + final Object[] heap; + PQueue(int maxSz) { - super.initialize(maxSz); + super(maxSz); + heap = getHeapArray(); myMaxSize = maxSz; } + @SuppressWarnings("unchecked") Iterable> getValues() { - return Collections.unmodifiableCollection(Arrays.asList(heap)); + return (Iterable) Collections.unmodifiableCollection(Arrays.asList(heap)); } @Override @@ -370,12 +374,13 @@ public class ConcurrentLRUCache { } // necessary because maxSize is private in base class + @SuppressWarnings("unchecked") public CacheEntry myInsertWithOverflow(CacheEntry element) { if (size() < myMaxSize) { add(element); return null; - } else if (size() > 0 && !lessThan(element, heap[1])) { - CacheEntry ret = heap[1]; + } else if (size() > 0 && !lessThan(element, (CacheEntry) heap[1])) { + CacheEntry ret = (CacheEntry) heap[1]; heap[1] = element; updateTop(); return ret; diff --git a/solr/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java b/solr/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java index c11b0ace7f3..b4c189d6033 100644 --- a/solr/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java +++ b/solr/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java @@ -586,7 +586,7 @@ public class LukeRequestHandler extends RequestHandlerBase public TermHistogram histogram; TopTermQueue(int size) { - initialize(size); + super(size); histogram = new TermHistogram(); } diff --git a/solr/src/java/org/apache/solr/handler/component/ShardDoc.java b/solr/src/java/org/apache/solr/handler/component/ShardDoc.java index 55e78cf4aa5..564cc00540e 100755 --- a/solr/src/java/org/apache/solr/handler/component/ShardDoc.java +++ b/solr/src/java/org/apache/solr/handler/component/ShardDoc.java @@ -82,6 +82,7 @@ class ShardFieldSortedHitQueue extends PriorityQueue { protected List fieldNames = new ArrayList(); public ShardFieldSortedHitQueue(SortField[] fields, int size) { + super(size); final int n = fields.length; comparators = new Comparator[n]; this.fields = new SortField[n]; @@ -107,8 +108,6 @@ class ShardFieldSortedHitQueue extends PriorityQueue { //System.out.println("%%%%%%%%%%%%%%%%%% got "+fields[i].getType() +" for "+ fieldname +" fields[i].getReverse(): "+fields[i].getReverse()); } - - initialize(size); } @Override diff --git a/solr/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java b/solr/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java index 56015fde2cf..0b003552f1a 100755 --- a/solr/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java +++ b/solr/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java @@ -114,10 +114,7 @@ class PerSegmentSingleValuedFaceting { // now merge the per-segment results - PriorityQueue queue = new PriorityQueue() { - { - initialize(leaves.length); - } + PriorityQueue queue = new PriorityQueue(leaves.length) { @Override protected boolean lessThan(SegFacet a, SegFacet b) { return a.tempBR.compareTo(b.tempBR) < 0; diff --git a/solr/src/java/org/apache/solr/spelling/suggest/Lookup.java b/solr/src/java/org/apache/solr/spelling/suggest/Lookup.java index a697fa5df00..c546f6a2d48 100644 --- a/solr/src/java/org/apache/solr/spelling/suggest/Lookup.java +++ b/solr/src/java/org/apache/solr/spelling/suggest/Lookup.java @@ -39,7 +39,7 @@ public abstract class Lookup { public static final class LookupPriorityQueue extends PriorityQueue { public LookupPriorityQueue(int size) { - initialize(size); + super(size); } @Override