/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * MetaMatrix, Inc - repackaging and updates for use as a metadata store *******************************************************************************/ package org.teiid.designer.core.index; import java.io.IOException; import java.util.Map; import org.teiid.core.designer.util.StringUtilities; /** * A mergeFactory is used to merge 2 indexes into one. One of the indexes * (oldIndex) is on the disk and the other(addsIndex) is in memory. * The merge respects the following rules: <br> * - The files are sorted in alphabetical order;<br> * - if a file is in oldIndex and addsIndex, the one which is added * is the one in the addsIndex.<br> * * @since 8.0 */ public class MergeFactory { /** * Input on the addsIndex. */ protected IndexInput addsInput; /** * Input on the oldIndex. */ protected IndexInput oldInput; /** * Output to write the result of the merge in. */ protected BlocksIndexOutput mergeOutput; /** * Files removed from oldIndex. */ protected Map removedInOld; /** * Files removed from addsIndex. */ protected Map removedInAdds; protected int[] mappingOld; protected int[] mappingAdds; public static final int ADDS_INDEX= 0; public static final int OLD_INDEX= 1; /** * MergeFactory constructor comment. * @param directory java.io.File */ public MergeFactory(IndexInput oldIndexInput, IndexInput addsIndexInput, BlocksIndexOutput mergeIndexOutput, Map removedInOld, Map removedInAdds) { oldInput= oldIndexInput; addsInput= addsIndexInput; mergeOutput= mergeIndexOutput; this.removedInOld= removedInOld; this.removedInAdds= removedInAdds; } /** * Initialise the merge. */ protected void init() { mappingOld= new int[oldInput.getNumFiles() + 1]; mappingAdds= new int[addsInput.getNumFiles() + 1]; } /** * Merges the 2 indexes into a new one on the disk. */ public void merge() throws IOException { try { //init addsInput.open(); oldInput.open(); mergeOutput.open(); init(); //merge //findChanges(); mergeFiles(); mergeReferences(); mergeOutput.flush(); } finally { //closes everything oldInput.close(); addsInput.close(); mergeOutput.close(); } } /** * Merges the files of the 2 indexes in the new index, removes the files * to be removed, and records the changes made to propagate them to the * word references. */ protected void mergeFiles() throws IOException { int positionInMerge= 1; int compare; while (oldInput.hasMoreFiles() || addsInput.hasMoreFiles()) { IndexedFile file1= oldInput.getCurrentFile(); IndexedFile file2= addsInput.getCurrentFile(); //if the file has been removed we don't take it into account while (file1 != null && wasRemoved(file1, OLD_INDEX)) { oldInput.moveToNextFile(); file1= oldInput.getCurrentFile(); } while (file2 != null && wasRemoved(file2, ADDS_INDEX)) { addsInput.moveToNextFile(); file2= addsInput.getCurrentFile(); } //the addsIndex was empty, we just removed files from the oldIndex if (file1 == null && file2 == null) break; //test if we reached the end of one the 2 index if (file1 == null) compare= 1; else if (file2 == null) compare= -1; else compare= file1.getPath().compareTo(file2.getPath()); //records the changes to Make if (compare == 0) { //the file has been modified: //we remove it from the oldIndex and add it to the addsIndex removeFile(file1, OLD_INDEX); mappingAdds[file2.getFileNumber()]= positionInMerge; file1.setFileNumber(positionInMerge); mergeOutput.addFile(file1); oldInput.moveToNextFile(); addsInput.moveToNextFile(); } else if (compare < 0) { mappingOld[file1.getFileNumber()]= positionInMerge; file1.setFileNumber(positionInMerge); mergeOutput.addFile(file1); oldInput.moveToNextFile(); } else { mappingAdds[file2.getFileNumber()]= positionInMerge; file2.setFileNumber(positionInMerge); mergeOutput.addFile(file2); addsInput.moveToNextFile(); } positionInMerge++; } mergeOutput.flushFiles(); } /** * Merges the files of the 2 indexes in the new index, according to the changes * recorded during mergeFiles(). */ protected void mergeReferences() throws IOException { int compare; while (oldInput.hasMoreWords() || addsInput.hasMoreWords()) { WordEntry word1= oldInput.getCurrentWordEntry(); WordEntry word2= addsInput.getCurrentWordEntry(); if (word1 == null && word2 == null) break; if (word1 == null) compare= 1; else if (word2 == null) compare= -1; else compare= StringUtilities.compare(word1.getWord(), word2.getWord()); if (compare < 0) { word1.mapRefs(mappingOld); mergeOutput.addWord(word1); oldInput.moveToNextWordEntry(); } else if (compare > 0) { word2.mapRefs(mappingAdds); mergeOutput.addWord(word2); addsInput.moveToNextWordEntry(); } else { word1.mapRefs(mappingOld); word2.mapRefs(mappingAdds); word1.addRefs(word2.getRefs()); mergeOutput.addWord(word1); addsInput.moveToNextWordEntry(); oldInput.moveToNextWordEntry(); } } mergeOutput.flushWords(); } /** * Records the deletion of one file. */ protected void removeFile(IndexedFile file, int index) { if (index == OLD_INDEX) mappingOld[file.getFileNumber()]= -1; else mappingAdds[file.getFileNumber()]= -1; } /** * Returns whether the given file has to be removed from the given index * (ADDS_INDEX or OLD_INDEX). If it has to be removed, the mergeFactory * deletes it and records the changes. */ protected boolean wasRemoved(IndexedFile indexedFile, int index) { String path= indexedFile.getPath(); if (index == OLD_INDEX) { if (removedInOld.remove(path) != null) { mappingOld[indexedFile.getFileNumber()]= -1; return true; } } else if (index == ADDS_INDEX) { int[] lastRemoved= (int[]) removedInAdds.get(path); if (lastRemoved != null) { int fileNum= indexedFile.getFileNumber(); if (lastRemoved[0] >= fileNum) { mappingAdds[fileNum]= -1; //if (lastRemoved.value == fileNum) // ONLY if files in sorted order for names AND fileNums //removedInAdds.remove(path); return true; } } } return false; } }