public int uncommittedChanges() { Set<Map.Entry<String, Storeable>> indexedSet = tableIndexedData.entrySet(); Set<Map.Entry<String, Storeable>> diskSet = tableOnDisk.entrySet(); int count = 0; Iterator<Map.Entry<String, Storeable>> iter1 = indexedSet.iterator(); Iterator<Map.Entry<String, Storeable>> iter2 = diskSet.iterator(); while (iter1.hasNext()) { Map.Entry<String, Storeable> next = iter1.next(); Storeable entryOnDisk = tableOnDisk.get(next.getKey()); if (entryOnDisk == null) { // записи на диске нет, то она изменение ++count; } else if (!entryOnDisk.equals(next.getValue())) { // запись на диске есть, но она другая, тоже изменение ++count; } } while (iter2.hasNext()) { Map.Entry<String, Storeable> next = iter2.next(); Storeable entryIndexed = tableIndexedData.get(next.getKey()); if (entryIndexed == null) { // на диске есть, индексированной нет, изменение ++count; } } return count; }
/** * Выполняет откат изменений с момента последней фиксации. * * @return Число откаченных изменений. */ @Override public int rollback() { int numberOfRolledChanges = uncommittedChanges(); tableIndexedData.clear(); tableIndexedData.putAll(tableOnDisk); for (int nDir = 0; nDir < 16; ++nDir) { for (int nFile = 0; nFile < 16; ++nFile) { tableFileModified[nDir][nFile] = false; } } return numberOfRolledChanges; }
/** * Устанавливает значение по указанному ключу. * * @param key Ключ для нового значения. Не может быть null. * @param value Новое значение. Не может быть null. * @return Значение, которое было записано по этому ключу ранее. Если ранее значения не было * записано, возвращает null. * @throws IllegalArgumentException Если значение параметров key или value является null. * @throws ru.fizteh.fivt.storage.structured.ColumnFormatException - при попытке передать * Storeable с колонками другого типа. */ @Override public Storeable put(String key, Storeable value) throws ColumnFormatException { checkKey(key); checkValue(value); Storeable oldValue = tableIndexedData.get(key); if (oldValue == null || !oldValue.equals(value)) { HashcodeDestination dest = new HashcodeDestination(key); tableFileModified[dest.getDir()][dest.getFile()] = true; tableIndexedData.put(key, value); } return oldValue; }
/** * Удаляет значение по указанному ключу. * * @param key Ключ для поиска значения. Не может быть null. * @return Предыдущее значение. Если не найдено, возвращает null. * @throws IllegalArgumentException Если значение параметра key является null. */ @Override public Storeable remove(String key) { checkKey(key); Storeable oldValue = tableIndexedData.remove(key); if (oldValue != null) { HashcodeDestination dest = new HashcodeDestination(key); tableFileModified[dest.getDir()][dest.getFile()] = true; } return oldValue; }
/** * Выполняет фиксацию изменений. * * @return Число записанных изменений. * @throws java.io.IOException если произошла ошибка ввода/вывода. Целостность таблицы не * гарантируется. */ @Override public int commit() throws IOException { int numberOfCommittedChanges = uncommittedChanges(); Set<Map.Entry<String, Storeable>> dbSet = tableIndexedData.entrySet(); for (int nDir = 0; nDir < 16; ++nDir) { for (int nFile = 0; nFile < 16; ++nFile) { if (tableFileModified[nDir][nFile]) { if (tableFiles[nDir][nFile] == null) { File subDir = new File(tableRootDir, Integer.toString(nDir) + ".dir"); File subFile = new File(subDir, Integer.toString(nFile) + ".dat"); if (!subDir.exists()) { if (!subDir.mkdir()) { throw new IllegalStateException("Sub dir was not created"); } } tableFiles[nDir][nFile] = new TableFile(subFile); } List<TableFile.Entry> fileData = new ArrayList<>(); Iterator<Map.Entry<String, Storeable>> iter = dbSet.iterator(); while (iter.hasNext()) { Map.Entry<String, Storeable> tempMapEntry = iter.next(); HashcodeDestination dest = new HashcodeDestination(tempMapEntry.getKey()); if (dest.getDir() == nDir && dest.getFile() == nFile) { fileData.add( new TableFile.Entry( tempMapEntry.getKey(), tableProvider.serialize(this, tempMapEntry.getValue()))); } } tableFiles[nDir][nFile].writeEntries(fileData); tableFileModified[nDir][nFile] = false; } } } tableOnDisk.clear(); tableOnDisk.putAll(tableIndexedData); return numberOfCommittedChanges; }
private void index() { File[] subDirsList = tableRootDir.listFiles(); if (subDirsList != null) { for (File subDir : subDirsList) { int numberOfSubDir; if (subDir.getName().equals(SignatureFile.signatureFileName)) { continue; } if (!subDir.isDirectory()) { throw new IllegalStateException("In table root dir found object is not a directory"); } String[] tableSubDirName = subDir.getName().split("[.]"); try { numberOfSubDir = Integer.parseInt(tableSubDirName[0]); } catch (NumberFormatException e) { throw new IllegalStateException("Table root directory contains not 0.dir ... 15.dir"); } if (numberOfSubDir < 0 || numberOfSubDir > 15 || !tableSubDirName[1].equals("dir") || tableSubDirName.length != 2) { throw new IllegalStateException("Table root directory contains not 0.dir ... 15.dir"); } File[] subFilesList = subDir.listFiles(); if (subFilesList != null && subFilesList.length == 0) { throw new IllegalStateException("data base contains empty dir"); } if (subFilesList != null) { for (File subFile : subFilesList) { int numberOfSubFile; if (!subFile.isFile()) { throw new IllegalStateException("In table sub dir found object is not a file"); } String[] dbFileName = subFile.getName().split("[.]"); try { numberOfSubFile = Integer.parseInt(dbFileName[0]); } catch (NumberFormatException e) { throw new IllegalStateException("Table sub directory contains not 0.dat ... 15.dat"); } if (numberOfSubFile < 0 || numberOfSubFile > 15 || !dbFileName[1].equals("dat") || dbFileName.length != 2) { throw new IllegalStateException("Table sub directory contains not 0.dat ... 15.dat"); } else if (subFile.length() == 0) { throw new IllegalStateException("Empty file in sub dir"); } else { tableFiles[numberOfSubDir][numberOfSubFile] = new TableFile(subFile); List<TableFile.Entry> fileData = tableFiles[numberOfSubDir][numberOfSubFile].readEntries(); for (TableFile.Entry i : fileData) { HashcodeDestination dest = new HashcodeDestination(i.getKey()); if (dest.getFile() != numberOfSubFile || dest.getDir() != numberOfSubDir) { throw new IllegalStateException("Wrong key placement"); } try { tableIndexedData.put(i.getKey(), tableProvider.deserialize(this, i.getValue())); } catch (ParseException e) { throw new IllegalStateException("Can't deserialize", e); } } } } } } } tableOnDisk = new HashMap<>(tableIndexedData); }
/** * Возвращает количество ключей в таблице. Возвращает размер текущей версии, с учётом * незафиксированных изменений. * * @return Количество ключей в таблице. */ @Override public int size() { return tableIndexedData.size(); }
/** * Получает значение по указанному ключу. * * @param key Ключ для поиска значения. Не может быть null. * @return Значение. Если не найдено, возвращает null. * @throws IllegalArgumentException Если значение параметра key является null. */ @Override public Storeable get(String key) { checkKey(key); return tableIndexedData.get(key); }