/*
* Copyright 2011 b1.org
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.b1.pack.standard.builder;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.b1.pack.api.builder.*;
import org.b1.pack.standard.common.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class StandardBuilderPack extends BuilderPack {
private final Map<ObjectKey, Long> objectIdMap = Maps.newHashMap();
private final List<PbRecord> catalogRecords = Lists.newArrayList();
private final List<PbRecord> completeRecords = Lists.newArrayList();
private final Map<Writable, PbRecordPointer> pointerMap = Maps.newHashMap();
private final long maxVolumeSize;
private final int blockOffsetSize;
private final PbRecordPointer catalogPointer;
private long objectCount;
public StandardBuilderPack(BuilderProvider provider) {
maxVolumeSize = provider.getMaxVolumeSize();
blockOffsetSize = Numbers.getSerializedSize(maxVolumeSize - 1);
catalogPointer = createPointer();
pointerMap.put(PbInt.NULL, catalogPointer);
}
@Override
public void addFolder(BuilderFolder folder) {
addFolderRecords(createHeader(folder), folder);
}
@Override
public void addFile(BuilderFile file) {
PbRecordHeader header = createHeader(file);
PbRecordPointer pointer = createPointer();
addRecords(pointer,
new PbRecord(new PbCatalogFile(pointer, header, file.getSize())),
new PbRecord(new PbCompleteFile(header, file)));
}
public List<BuilderVolume> getVolumes() {
VolumeBuilder builder = new VolumeBuilder(maxVolumeSize, pointerMap, objectCount);
builder.addContent(createCatalog());
for (PbRecord record : completeRecords) {
builder.addContent(record);
}
builder.addContent(PbInt.NULL);
return builder.getVolumes();
}
private PbRecordPointer createPointer() {
return new PbRecordPointer(Numbers.MAX_INT_SIZE, blockOffsetSize, 1);
}
private PbRecordHeader createHeader(BuilderObject object) {
LinkedList<String> path = Lists.newLinkedList(object.getPath());
String name = path.removeLast();
Long parentId = null;
for (String s : path) {
ObjectKey key = new ObjectKey(parentId, s);
Long id = objectIdMap.get(key);
if (id == null) {
id = createNewId(key);
addFolderRecords(new PbRecordHeader(id, parentId, s, System.currentTimeMillis()), null);
}
parentId = id;
}
ObjectKey key = new ObjectKey(parentId, name);
Preconditions.checkArgument(!objectIdMap.containsKey(key), "Path already exists");
return new PbRecordHeader(createNewId(key), parentId, name, object.getLastModifiedTime());
}
private Long createNewId(ObjectKey key) {
Long id = ++objectCount;
objectIdMap.put(key, id);
return id;
}
private void addFolderRecords(PbRecordHeader header, BuilderFolder folder) {
PbRecordPointer pointer = createPointer();
addRecords(pointer,
new PbRecord(new PbCatalogFolder(pointer, header)),
new PbRecord(new PbCompleteFolder(header, folder)));
}
private void addRecords(PbRecordPointer pointer, PbRecord catalogRecord, PbRecord completeRecord) {
pointerMap.put(completeRecord, pointer);
catalogRecords.add(catalogRecord);
completeRecords.add(completeRecord);
}
private Writable createCatalog() {
PbRecord[] catalog = new PbRecord[catalogRecords.size() + 1];
catalogRecords.toArray(catalog);
catalog[catalog.length - 1] = new PbRecord(catalogPointer);
return new CompositeWritable(catalog);
}
}