blob: 81fda00ef492c6ad55c762e9bdbcd8f5d5d2f928 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.registry;
import java.io.*;
import java.util.HashMap;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
public class TableReader {
//Markers in the cache
static final int NULL = 0;
static final int OBJECT = 1;
//The version of the cache
static final int CACHE_VERSION = 1;
//Informations representing the MAIN file
static final String MAIN = ".mainData"; //$NON-NLS-1$
File mainDataFile;
DataInputStream mainInput = null;
// int size;
//Informations representing the EXTRA file
static final String EXTRA = ".extraData"; //$NON-NLS-1$
File extraDataFile;
DataInputStream extraInput = null;
// int sizeExtra;
//The table file
static final String TABLE = ".table"; //$NON-NLS-1$
File tableFile;
//The namespace file
static final String CONTRIBUTIONS = ".contributions"; //$NON-NLS-1$
File contributionsFile;
//The orphan file
static final String ORPHANS = ".orphans"; //$NON-NLS-1$
File orphansFile;
//Status code
private static final byte fileError = 0;
private static final boolean DEBUG = false; //TODO need to change
private boolean holdObjects = false;
private ExtensionRegistry registry;
void setMainDataFile(File main) {
mainDataFile = main;
}
void setExtraDataFile(File extra) {
extraDataFile = extra;
}
void setTableFile(File table) {
tableFile = table;
}
void setContributionsFile(File namespace) {
contributionsFile = namespace;
}
void setOrphansFile(File orphan) {
orphansFile = orphan;
}
public TableReader(ExtensionRegistry registry) {
this.registry = registry;
}
private void openInputFile() {
try {
mainInput = new DataInputStream(new BufferedInputStream(new FileInputStream(mainDataFile)));
} catch (FileNotFoundException e) {
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, RegistryMessages.meta_unableToReadCache, e));
registry.clearRegistryCache();
throw new IllegalStateException(RegistryMessages.meta_registryCacheReadProblems);
}
}
private void openExtraFile() {
try {
extraInput = new DataInputStream(new BufferedInputStream(new FileInputStream(extraDataFile)));
} catch (FileNotFoundException e) {
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, RegistryMessages.meta_unableToReadCache, e));
registry.clearRegistryCache();
throw new IllegalStateException(RegistryMessages.meta_registryCacheReadProblems);
}
}
private void closeInputFile() {
try {
mainInput.close();
} catch (IOException e) {
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, RegistryMessages.meta_registryCacheReadProblems, e));
}
}
private void closeExtraFile() {
try {
extraInput.close();
} catch (IOException e) {
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, RegistryMessages.meta_registryCacheReadProblems, e));
}
}
/**
* Resets position in the main input file and extra input files to the
* begining of the stream; sets holdObjects flag to a default value (false).
*/
public void reset() {
if (extraInput != null)
closeExtraFile();
if (mainInput != null)
closeInputFile();
holdObjects = false;
openInputFile();
openExtraFile();
}
public Object[] loadTables(long expectedTimestamp) {
HashtableOfInt offsets;
HashtableOfStringAndInt extensionPoints;
DataInputStream tableInput = null;
try {
tableInput = new DataInputStream(new BufferedInputStream(new FileInputStream(tableFile)));
if (!checkCacheValidity(tableInput, expectedTimestamp))
return null;
Integer nextId = new Integer(tableInput.readInt());
offsets = new HashtableOfInt();
offsets.load(tableInput);
extensionPoints = new HashtableOfStringAndInt();
extensionPoints.load(tableInput);
return new Object[] {offsets, extensionPoints, nextId};
} catch (IOException e) {
if (tableInput != null)
try {
tableInput.close();
} catch (IOException e1) {
//Ignore
}
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, RegistryMessages.meta_registryCacheReadProblems, e));
return null;
}
}
// Check various aspect of the cache to see if it's valid
private boolean checkCacheValidity(DataInputStream in, long expectedTimestamp) {
int version;
try {
version = in.readInt();
if (version != CACHE_VERSION)
return false;
long installStamp = in.readLong();
long registryStamp = in.readLong();
long mainDataFileSize = in.readLong();
long extraDataFileSize = in.readLong();
long contributionsFileSize = in.readLong();
long orphansFileSize = in.readLong();
String osStamp = in.readUTF();
String windowsStamp = in.readUTF();
String localeStamp = in.readUTF();
long timeStamp = registry.computeState();
return ((expectedTimestamp == 0 || expectedTimestamp == registryStamp) && (installStamp == timeStamp) && (osStamp.equals(System.getProperty(IRegistryConstants.PROP_OS))) && (windowsStamp.equals(System.getProperty(IRegistryConstants.PROP_WS))) && (localeStamp.equals(System.getProperty(IRegistryConstants.PROP_NL))) && mainDataFileSize == mainDataFile.length() && extraDataFileSize == extraDataFile.length() && contributionsFileSize == contributionsFile.length() && orphansFileSize == orphansFile.length());
} catch (IOException e) {
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, RegistryMessages.meta_registryCacheInconsistent, e));
return false;
}
}
public Object loadConfigurationElement(int offset) {
try {
goToInputFile(offset);
return basicLoadConfigurationElement(mainInput, -1);
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, mainDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
if (DEBUG)
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, "Error reading a configuration element (" + offset + ") from the registry cache", e)); //$NON-NLS-1$//$NON-NLS-2$
return null;
} finally {
closeInputFile();
closeExtraFile();
}
}
private ConfigurationElement basicLoadConfigurationElement(DataInputStream is, long namespaceOwnerId) throws IOException {
int self = is.readInt();
long contributorId = is.readLong();
String name = readStringOrNull(is, false);
int parentId = is.readInt();
byte parentType = is.readByte();
int misc = is.readInt();//this is set in second level CEs, to indicate where in the extra data file the children ces are
String[] propertiesAndValue = readPropertiesAndValue(is);
int[] children = readArray(is);
return getObjectFactory().createConfigurationElement(self, contributorId, namespaceOwnerId, name, propertiesAndValue, children, misc, parentId, parentType);
}
public Object loadThirdLevelConfigurationElements(int offset, RegistryObjectManager objectManager) {
try {
goToExtraFile(offset);
return loadConfigurationElementAndChildren(null, extraInput, 3, Integer.MAX_VALUE, objectManager, -1);
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, extraDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
if (DEBUG)
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, "Error reading a third level configuration element (" + offset + ") from the registry cache", e)); //$NON-NLS-1$//$NON-NLS-2$
return null;
} finally {
closeInputFile();
closeExtraFile();
}
}
//Read a whole configuration element subtree
private ConfigurationElement loadConfigurationElementAndChildren(DataInputStream is, DataInputStream extraIs, int depth, int maxDepth, RegistryObjectManager objectManager, long namespaceOwnerId) throws IOException {
DataInputStream currentStream = is;
if (depth > 2)
currentStream = extraIs;
ConfigurationElement ce = basicLoadConfigurationElement(currentStream, namespaceOwnerId);
if (namespaceOwnerId == -1)
namespaceOwnerId = ce.getNamespaceOwnerId();
int[] children = ce.getRawChildren();
if (depth + 1 > maxDepth)
return ce;
for (int i = 0; i < children.length; i++) {
ConfigurationElement tmp = loadConfigurationElementAndChildren(currentStream, extraIs, depth + 1, maxDepth, objectManager, namespaceOwnerId);
objectManager.add(tmp, holdObjects);
}
return ce;
}
private String[] readPropertiesAndValue(DataInputStream inputStream) throws IOException {
int numberOfProperties = inputStream.readInt();
if (numberOfProperties == 0)
return RegistryObjectManager.EMPTY_STRING_ARRAY;
String[] properties = new String[numberOfProperties];
for (int i = 0; i < numberOfProperties; i++) {
properties[i] = readStringOrNull(inputStream, false);
}
return properties;
}
public Object loadExtension(int offset) {
try {
goToInputFile(offset);
return basicLoadExtension(mainInput);
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, mainDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
if (DEBUG)
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, "Error reading an extension (" + offset + ") from the registry cache", e)); //$NON-NLS-1$//$NON-NLS-2$
} finally {
closeExtraFile();
closeInputFile();
}
return null;
}
private Extension basicLoadExtension(DataInputStream inputStream) throws IOException {
int self = inputStream.readInt();
String simpleId = readStringOrNull(mainInput, false);
String namespace = readStringOrNull(mainInput, false);
int[] children = readArray(mainInput);
int extraData = mainInput.readInt();
return getObjectFactory().createExtension(self, simpleId, namespace, children, extraData);
}
public ExtensionPoint loadExtensionPointTree(int offset, RegistryObjectManager objects) {
try {
ExtensionPoint xpt = (ExtensionPoint) loadExtensionPoint(offset);
int[] children = xpt.getRawChildren();
int nbrOfExtension = children.length;
for (int i = 0; i < nbrOfExtension; i++) {
Extension loaded = basicLoadExtension(mainInput);
objects.add(loaded, holdObjects);
}
for (int i = 0; i < nbrOfExtension; i++) {
int nbrOfCe = mainInput.readInt();
for (int j = 0; j < nbrOfCe; j++) {
objects.add(loadConfigurationElementAndChildren(mainInput, extraInput, 1, 2, objects, -1), holdObjects);
}
}
return xpt;
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, mainDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
if (DEBUG)
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, "Error reading an extension point tree (" + offset + ") from the registry cache", e)); //$NON-NLS-1$//$NON-NLS-2$
return null;
} finally {
closeExtraFile();
closeInputFile();
}
}
private Object loadExtensionPoint(int offset) {
try {
goToInputFile(offset);
return basicLoadExtensionPoint();
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, mainDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
if (DEBUG)
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, "Error reading an extension point (" + offset + ") from the registry cache", e)); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
}
private ExtensionPoint basicLoadExtensionPoint() throws IOException {
int self = mainInput.readInt();
int[] children = readArray(mainInput);
int extraData = mainInput.readInt();
return getObjectFactory().createExtensionPoint(self, children, extraData);
}
private int[] readArray(DataInputStream in) throws IOException {
int arraySize = in.readInt();
if (arraySize == 0)
return RegistryObjectManager.EMPTY_INT_ARRAY;
int[] result = new int[arraySize];
for (int i = 0; i < arraySize; i++) {
result[i] = in.readInt();
}
return result;
}
private void goToInputFile(int offset) throws IOException {
mainInput.skipBytes(offset);
}
private void goToExtraFile(int offset) throws IOException {
extraInput.skipBytes(offset);
}
private String readStringOrNull(DataInputStream in, boolean intern) throws IOException {
byte type = in.readByte();
if (type == NULL)
return null;
if (intern)
return in.readUTF().intern();
return in.readUTF();
}
public String[] loadExtensionExtraData(int dataPosition) {
try {
goToExtraFile(dataPosition);
return basicLoadExtensionExtraData();
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, extraDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
if (DEBUG)
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, "Error reading extension label (" + dataPosition + ") from the registry cache", e)); //$NON-NLS-1$ //$NON-NLS-2$
return null;
} finally {
closeExtraFile();
closeInputFile();
}
}
private String[] basicLoadExtensionExtraData() throws IOException {
return new String[] {readStringOrNull(extraInput, false), readStringOrNull(extraInput, false)};
}
public String[] loadExtensionPointExtraData(int offset) {
try {
goToExtraFile(offset);
return basicLoadExtensionPointExtraData();
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, extraDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
if (DEBUG)
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, "Error reading extension point data (" + offset + ") from the resgistry cache", e)); //$NON-NLS-1$ //$NON-NLS-2$
return null;
} finally {
closeExtraFile();
closeInputFile();
}
}
private String[] basicLoadExtensionPointExtraData() throws IOException {
String[] result = new String[5];
result[0] = readStringOrNull(extraInput, false); //the label
result[1] = readStringOrNull(extraInput, false); //the schema
result[2] = readStringOrNull(extraInput, false); //the fully qualified name
result[3] = readStringOrNull(extraInput, false); //the namespace
result[4] = Long.toString(extraInput.readLong());
return result;
}
public KeyedHashSet loadNamespaces() {
DataInputStream namespaceInput = null;
try {
namespaceInput = new DataInputStream(new BufferedInputStream(new FileInputStream(contributionsFile)));
int size = namespaceInput.readInt();
KeyedHashSet result = new KeyedHashSet(size);
for (int i = 0; i < size; i++) {
Contribution n = getObjectFactory().createContribution(namespaceInput.readLong());
n.setRawChildren(readArray(namespaceInput));
result.add(n);
}
return result;
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, contributionsFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
return null;
} finally {
if (namespaceInput != null)
try {
namespaceInput.close();
} catch (IOException e1) {
//Ignore
}
}
}
private void loadAllOrphans(RegistryObjectManager objectManager) throws IOException {
//Read the extensions and configuration elements of the orphans
int orphans = objectManager.getOrphanExtensions().size();
for (int k = 0; k < orphans; k++) {
int numberOfOrphanExtensions = mainInput.readInt();
for (int i = 0; i < numberOfOrphanExtensions; i++) {
loadFullExtension(objectManager);
}
for (int i = 0; i < numberOfOrphanExtensions; i++) {
int nbrOfCe = mainInput.readInt();
for (int j = 0; j < nbrOfCe; j++) {
objectManager.add(loadConfigurationElementAndChildren(mainInput, extraInput, 1, Integer.MAX_VALUE, objectManager, -1), true);
}
}
}
}
public boolean readAllCache(RegistryObjectManager objectManager) {
try {
int size = objectManager.getExtensionPoints().size();
for (int i = 0; i < size; i++) {
objectManager.add(readAllExtensionPointTree(objectManager), holdObjects);
}
loadAllOrphans(objectManager);
} catch (IOException e) {
String message = NLS.bind(RegistryMessages.meta_regCacheIOExceptionReading, mainDataFile);
log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, fileError, message, e));
return false;
} finally {
closeExtraFile();
closeInputFile();
}
return true;
}
public ExtensionPoint readAllExtensionPointTree(RegistryObjectManager objectManager) throws IOException {
ExtensionPoint xpt = loadFullExtensionPoint();
int[] children = xpt.getRawChildren();
int nbrOfExtension = children.length;
for (int i = 0; i < nbrOfExtension; i++) {
loadFullExtension(objectManager);
}
for (int i = 0; i < nbrOfExtension; i++) {
int nbrOfCe = mainInput.readInt();
for (int j = 0; j < nbrOfCe; j++) {
objectManager.add(loadConfigurationElementAndChildren(mainInput, extraInput, 1, Integer.MAX_VALUE, objectManager, -1), true);
}
}
return xpt;
}
private ExtensionPoint loadFullExtensionPoint() throws IOException { //TODO I don't like this.
ExtensionPoint xpt = basicLoadExtensionPoint();
String[] tmp = basicLoadExtensionPointExtraData();
xpt.setLabel(tmp[0]);
xpt.setSchema(tmp[1]);
xpt.setUniqueIdentifier(tmp[2]);
xpt.setNamespace(tmp[3]);
xpt.setNamespaceOwnerId(Long.parseLong(tmp[4]));
return xpt;
}
private Extension loadFullExtension(RegistryObjectManager objectManager) throws IOException {
String[] tmp;
Extension loaded = basicLoadExtension(mainInput);
tmp = basicLoadExtensionExtraData();
loaded.setLabel(tmp[0]);
loaded.setExtensionPointIdentifier(tmp[1]);
objectManager.add(loaded, holdObjects);
return loaded;
}
public HashMap loadOrphans() {
DataInputStream orphanInput = null;
try {
orphanInput = new DataInputStream(new BufferedInputStream(new FileInputStream(orphansFile)));
int size = orphanInput.readInt();
HashMap result = new HashMap(size);
for (int i = 0; i < size; i++) {
String key = orphanInput.readUTF();
int[] value = readArray(orphanInput);
result.put(key, value);
}
return result;
} catch (IOException e) {
return null;
} finally {
if (orphanInput != null)
try {
orphanInput.close();
} catch (IOException e1) {
//ignore
}
}
}
public void setHoldObjects(boolean holdObjects) {
this.holdObjects = holdObjects;
}
private void log(Status status) {
registry.log(status);
}
private RegistryObjectFactory getObjectFactory() {
return registry.getElementFactory();
}
// Returns a file name used to test if cache is actually present at a given location
public static String getTestFileName() {
return TABLE;
}
}