Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.search.impl.lucene; import java.io.IOException; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.alfresco.error.AlfrescoRuntimeException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.FilterIndexReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.TermEnum; import org.apache.lucene.index.TermPositions; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Searcher; import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.OpenBitSet; /** * An index reader that filters documents from another. * * @author andyh * */ public class FilterIndexReaderByStringId extends FilterIndexReader { private static Log s_logger = LogFactory.getLog(FilterIndexReaderByStringId.class); private OpenBitSet deletedDocuments; private final Set<String> deletions; private final Set<String> containerDeletions; private final boolean deleteNodesOnly; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final String id; /** * Apply the filter * * @param id String * @param reader IndexReader * @param deleteNodesOnly boolean */ public FilterIndexReaderByStringId(String id, IndexReader reader, Set<String> deletions, Set<String> containerDeletions, boolean deleteNodesOnly) { super(reader); reader.incRef(); this.id = id; this.deletions = deletions; this.containerDeletions = containerDeletions; this.deleteNodesOnly = deleteNodesOnly; if (s_logger.isDebugEnabled()) { s_logger.debug("Applying deletions FOR " + id + " (the index ito which these are applied is the previous one ...)"); } } public OpenBitSet getDeletedDocuments() { lock.readLock().lock(); try { if (deletedDocuments != null) { return deletedDocuments; } } finally { lock.readLock().unlock(); } lock.writeLock().lock(); try { if (deletedDocuments != null) { return deletedDocuments; } deletedDocuments = new OpenBitSet(in.maxDoc()); Searcher searcher = new IndexSearcher(in); for (String stringRef : deletions) { if (!deleteNodesOnly || containerDeletions.contains(stringRef)) { TermDocs td = in.termDocs(new Term("ID", stringRef)); while (td.next()) { deletedDocuments.set(td.doc()); } td.close(); } else { boolean found = false; TermDocs td = in.termDocs(new Term("LEAFID", stringRef)); while (td.next()) { deletedDocuments.set(td.doc()); found = true; } td.close(); // For backward compatibility, use old method of locating non-container docs if (!found) { TermQuery query = new TermQuery(new Term("ID", stringRef)); Hits hits = searcher.search(query); if (hits.length() > 0) { for (int i = 0; i < hits.length(); i++) { Document doc = hits.doc(i); // Exclude all containers except the root (which is also a node!) Field path = doc.getField("PATH"); if (path == null || path.stringValue().length() == 0) { deletedDocuments.set(hits.id(i)); // There should only be one thing to delete // break; } } } } } } // searcher does not need to be closed, the reader is live for (String stringRef : containerDeletions) { TermDocs td = in.termDocs(new Term("ANCESTOR", stringRef)); while (td.next()) { deletedDocuments.set(td.doc()); } td.close(); } return deletedDocuments; } catch (IOException e) { s_logger.error("Error initialising " + id, e); throw new AlfrescoRuntimeException("Failed to find deleted documents to filter", e); } finally { lock.writeLock().unlock(); } } // Prevent from actually setting the closed flag @Override protected void doClose() throws IOException { this.in.decRef(); } /** * Filter implementation * * @author andyh * */ public class FilterTermDocs implements TermDocs { protected TermDocs in; String id; /** * @param id String * @param in TermDocs */ public FilterTermDocs(String id, TermDocs in) { this.in = in; } public void seek(Term term) throws IOException { // Seek is left to the base implementation in.seek(term); } public void seek(TermEnum termEnum) throws IOException { // Seek is left to the base implementation in.seek(termEnum); } public int doc() { // The current document info is valid in the base implementation return in.doc(); } public int freq() { // The frequency is valid in the base implementation return in.freq(); } public boolean next() throws IOException { try { if (!in.next()) { return false; } OpenBitSet deletedDocuments = getDeletedDocuments(); while (deletedDocuments.get(in.doc())) { if (!in.next()) { return false; } } // Not masked return true; } catch (IOException ioe) { s_logger.error("Error reading docs for " + id); throw ioe; } } public int read(int[] docs, int[] freqs) throws IOException { int[] innerDocs = new int[docs.length]; int[] innerFreq = new int[docs.length]; int count = in.read(innerDocs, innerFreq); // Is the stream exhausted if (count == 0) { return 0; } OpenBitSet deletedDocuments = getDeletedDocuments(); while (allDeleted(innerDocs, count, deletedDocuments)) { count = in.read(innerDocs, innerFreq); // Is the stream exhausted if (count == 0) { return 0; } } // Add non deleted int insertPosition = 0; for (int i = 0; i < count; i++) { if (!deletedDocuments.get(innerDocs[i])) { docs[insertPosition] = innerDocs[i]; freqs[insertPosition] = innerFreq[i]; insertPosition++; } } return insertPosition; } private boolean allDeleted(int[] docs, int fillSize, OpenBitSet deletedDocuments) { for (int i = 0; i < fillSize; i++) { if (!deletedDocuments.get(docs[i])) { return false; } } return true; } public boolean skipTo(int i) throws IOException { if (!in.skipTo(i)) { return false; } OpenBitSet deletedDocuments = getDeletedDocuments(); while (deletedDocuments.get(in.doc())) { if (!in.next()) { return false; } } return true; } public void close() throws IOException { // Leave to internal implementation in.close(); } } /** Base class for filtering {@code TermPositions} implementations. */ public class FilterTermPositions extends FilterTermDocs implements TermPositions { TermPositions tp; /** * @param id String * @param in TermPositions */ public FilterTermPositions(String id, TermPositions in) { super(id, in); tp = in; } public int nextPosition() throws IOException { return tp.nextPosition(); } public byte[] getPayload(byte[] data, int offset) throws IOException { return tp.getPayload(data, offset); } public int getPayloadLength() { return tp.getPayloadLength(); } public boolean isPayloadAvailable() { return tp.isPayloadAvailable(); } } @Override public int numDocs() { return super.numDocs() - (int) getDeletedDocuments().cardinality(); } @Override public TermDocs termDocs() throws IOException { return new FilterTermDocs(id, super.termDocs()); } @Override public TermPositions termPositions() throws IOException { return new FilterTermPositions(id, super.termPositions()); } }