/*
* Copyright (c) 2014, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.internal.index.file;
import com.google.common.collect.Maps;
import com.google.dart.engine.context.AnalysisContext;
import com.google.dart.engine.utilities.logging.Logger;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* A {@link FileManager} based {@link NodeManager}.
*
* @coverage dart.engine.index
*/
public class FileNodeManager implements NodeManager {
private static int VERSION = 1;
private final FileManager fileManager;
private final Logger logger;
private final StringCodec stringCodec;
private final ContextCodec contextCodec;
private final ElementCodec elementCodec;
private final RelationshipCodec relationshipCodec;
private final Map<String, Integer> nodeLocationCounts = Maps.newHashMap();
private int locationCount = 0;
public FileNodeManager(FileManager fileManager, Logger logger, StringCodec stringCodec,
ContextCodec contextCodec, ElementCodec elementCodec, RelationshipCodec relationshipCodec) {
this.fileManager = fileManager;
this.logger = logger;
this.stringCodec = stringCodec;
this.contextCodec = contextCodec;
this.elementCodec = elementCodec;
this.relationshipCodec = relationshipCodec;
}
@Override
public void clear() {
fileManager.clear();
}
@Override
public ContextCodec getContextCodec() {
return contextCodec;
}
@Override
public ElementCodec getElementCodec() {
return elementCodec;
}
@Override
public int getLocationCount() {
return locationCount;
}
@Override
public IndexNode getNode(String name) {
try {
InputStream inputStream = fileManager.openInputStream(name);
if (inputStream != null) {
try {
DataInputStream stream = new DataInputStream(inputStream);
// check version
{
int version = stream.readInt();
if (version != VERSION) {
throw new IllegalStateException("Version " + VERSION + " expected, but " + version
+ " found.");
}
}
// context
int contextId = stream.readInt();
AnalysisContext context = contextCodec.decode(contextId);
if (context == null) {
return null;
}
// relations
Map<RelationKeyData, List<LocationData>> relations = Maps.newHashMap();
int numRelations = stream.readInt();
for (int i = 0; i < numRelations; i++) {
RelationKeyData key = readElementRelationKey(stream);
int numLocations = stream.readInt();
List<LocationData> locations = new ArrayList<LocationData>(numLocations);
for (int j = 0; j < numLocations; j++) {
locations.add(readLocationData(stream));
}
relations.put(key, locations);
}
// create IndexNode
IndexNode node = new IndexNode(context, elementCodec, relationshipCodec);
node.setRelations(relations);
return node;
} finally {
inputStream.close();
}
}
} catch (Throwable e) {
logger.logError("Exception during reading index file " + name, e);
}
return null;
}
@Override
public StringCodec getStringCodec() {
return stringCodec;
}
@Override
public IndexNode newNode(AnalysisContext context) {
return new IndexNode(context, elementCodec, relationshipCodec);
}
@Override
public void putNode(String name, IndexNode node) {
// update location count
{
locationCount -= getLocationCount(name);
int nodeLocationCount = node.getLocationCount();
nodeLocationCounts.put(name, nodeLocationCount);
locationCount += nodeLocationCount;
}
// write the node
try {
OutputStream stream = fileManager.openOutputStream(name);
try {
writeNode(node, stream);
} finally {
stream.close();
}
} catch (Throwable e) {
logger.logError("Exception during writing index file " + name, e);
}
}
@Override
public void removeNode(String name) {
// update location count
locationCount -= getLocationCount(name);
nodeLocationCounts.remove(name);
// remove node
fileManager.delete(name);
}
private int getLocationCount(String name) {
Integer locationCount = nodeLocationCounts.get(name);
return locationCount != null ? locationCount : 0;
}
private RelationKeyData readElementRelationKey(DataInputStream stream) throws Exception {
int elementId = stream.readInt();
int relationshipId = stream.readInt();
return new RelationKeyData(elementId, relationshipId);
}
private LocationData readLocationData(DataInputStream stream) throws Exception {
int elementId = stream.readInt();
int offset = stream.readInt();
int length = stream.readInt();
return new LocationData(elementId, offset, length);
}
private void writeElementRelationKey(DataOutputStream stream, RelationKeyData key)
throws Exception {
stream.writeInt(key.elementId);
stream.writeInt(key.relationshipId);
}
private void writeNode(IndexNode node, OutputStream outputStream) throws Exception {
DataOutputStream stream = new DataOutputStream(outputStream);
// version
stream.writeInt(VERSION);
// context
{
AnalysisContext context = node.getContext();
int contextId = contextCodec.encode(context);
stream.writeInt(contextId);
}
// relations
Map<RelationKeyData, List<LocationData>> relations = node.getRelations();
stream.writeInt(relations.size());
for (Entry<RelationKeyData, List<LocationData>> entry : relations.entrySet()) {
RelationKeyData key = entry.getKey();
List<LocationData> locations = entry.getValue();
writeElementRelationKey(stream, key);
stream.writeInt(locations.size());
for (LocationData location : locations) {
stream.writeInt(location.elementId);
stream.writeInt(location.offset);
stream.writeInt(location.length);
}
}
}
}