/* * Copyright 2011 Outerthought bvba * * 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.lilyproject.repository.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.lilyproject.repository.api.IdGenerator; import org.lilyproject.repository.api.LTable; import org.lilyproject.repository.api.Metadata; import org.lilyproject.repository.api.MutationCondition; import org.lilyproject.repository.api.QName; import org.lilyproject.repository.api.Record; import org.lilyproject.repository.api.RecordBuilder; import org.lilyproject.repository.api.RecordException; import org.lilyproject.repository.api.RecordId; import org.lilyproject.repository.api.RepositoryException; import org.lilyproject.util.ArgumentValidator; /** * */ public class RecordBuilderImpl implements RecordBuilder { private final LTable table; private final IdGenerator idGenerator; private Record record; private List<MutationCondition> mutationConditions = null; private boolean updateVersion = false; private boolean useLatestRecordType = true; private String defaultNamespace; // Fields related to nested builders private RecordBuilderImpl parent; private QName parentField; private Mode mode; private List<Record> records; private enum Mode {ROOT_RECORD, NESTED_RECORD, NESTED_RECORD_LIST} public RecordBuilderImpl(LTable table, IdGenerator idGenerator) throws RecordException { this.table = table; this.idGenerator = idGenerator; this.mode = Mode.ROOT_RECORD; this.record = this.table.newRecord(); } public RecordBuilderImpl(RecordBuilderImpl parent, QName parentField, Mode mode) throws RecordException { this.table = parent.table; this.idGenerator = parent.idGenerator; this.mode = mode; this.parent = parent; this.parentField = parentField; this.record = table.newRecord(); defaultNamespace(parent.defaultNamespace); } @Override public RecordBuilder defaultNamespace(String namespace) { this.defaultNamespace = namespace; record.setDefaultNamespace(namespace); return this; } @Override public RecordBuilder field(QName name, Object value) { ArgumentValidator.notNull(name, "name"); record.setField(name, value); return this; } @Override public RecordBuilder field(String name, Object value) throws RecordException { ArgumentValidator.notNull(name, "name"); record.setField(name, value); return this; } @Override public RecordBuilder mutationCondition(MutationCondition condition) { ArgumentValidator.notNull(condition, "condition"); if (mutationConditions == null) { this.mutationConditions = new ArrayList<MutationCondition>(); } mutationConditions.add(condition); return this; } @Override public RecordBuilder id(RecordId id) { record.setId(id); return this; } @Override public RecordBuilder id(RecordId id, Map<String, String> variantProperties) { record.setId(idGenerator.newRecordId(id.getMaster(), variantProperties)); return this; } @Override public RecordBuilder id(String userId) { record.setId(idGenerator.newRecordId(userId)); return this; } @Override public RecordBuilder id(String userId, Map<String, String> variantProperties) { record.setId(idGenerator.newRecordId(userId, variantProperties)); return this; } @Override public RecordBuilder assignNewUuid() { record.setId(idGenerator.newRecordId()); return this; } @Override public RecordBuilder assignNewUuid(Map<String, String> variantProperties) { record.setId(idGenerator.newRecordId(variantProperties)); return this; } @Override public RecordBuilder recordType(QName name) { return recordType(name, null); } @Override public RecordBuilder recordType(QName name, Long version) { record.setRecordType(name, version); return this; } @Override public RecordBuilder recordType(String name) throws RecordException { return recordType(name, null); } @Override public RecordBuilder recordType(String name, Long version) throws RecordException { record.setRecordType(name, version); return this; } @Override public RecordBuilder updateVersion(boolean updateVersion) { this.updateVersion = updateVersion; return this; } @Override public RecordBuilder useLatestRecordType(boolean latestRT) { this.useLatestRecordType = latestRT; return this; } @Override public RecordBuilder attribute(String name, String value) { record.getAttributes().put(name, value); return this; } @Override public RecordBuilder version(Long version) { record.setVersion(version); return this; } @Override public RecordBuilder metadata(QName fieldName, Metadata metadata) throws RecordException { if (mode == Mode.ROOT_RECORD) { record.setMetadata(fieldName, metadata); } else { throw new IllegalStateException("Metadata is only supported for top-level records."); } return this; } @Override public RecordBuilder reset() throws RecordException { record = table.newRecord(); mutationConditions = null; updateVersion = false; useLatestRecordType = true; return this; } private Record createRecord() { return record; } @Override public Record build() { if (mode == Mode.ROOT_RECORD) { return createRecord(); } else { throw new IllegalStateException("update should only be called for root records, current mode is " + mode); } } @Override public Record update() throws RepositoryException, InterruptedException { if (mode == Mode.ROOT_RECORD) { return table.update(record, updateVersion, useLatestRecordType, mutationConditions); } else { throw new IllegalStateException("update should only be called for root records, current mode is " + mode); } } @Override public Record create() throws RepositoryException, InterruptedException { if (mode == Mode.ROOT_RECORD) { return table.create(record); } else { throw new IllegalStateException("update should only be called for root records, current mode is " + mode); } } @Override public Record createOrUpdate() throws RepositoryException, InterruptedException { if (mode == Mode.ROOT_RECORD) { return table.createOrUpdate(record, useLatestRecordType); } else { throw new IllegalStateException("update should only be called for root records, current mode is " + mode); } } @Override public RecordBuilder recordField(String name) throws RecordException { return recordField(resolveNamespace(name)); } @Override public RecordBuilder recordField(QName name) throws RecordException { return new RecordBuilderImpl(this, name, Mode.NESTED_RECORD); } @Override public RecordBuilder set() { if (mode == Mode.NESTED_RECORD) { parent.record.setField(parentField, createRecord()); return parent; } else { throw new IllegalStateException("set should only be called for nested records, current mode is " + mode); } } @Override public RecordBuilder recordListField(String name) throws RecordException { return recordListField(resolveNamespace(name)); } @Override public RecordBuilder recordListField(QName name) throws RecordException { return new RecordBuilderImpl(this, name, Mode.NESTED_RECORD_LIST); } @Override public RecordBuilder add() throws RecordException { if (mode == Mode.NESTED_RECORD_LIST) { if (records == null) { records = new ArrayList<Record>(); } records.add(createRecord()); // Reset, but keep the record type setting (and the default namespace) QName prevRecordTypeName = record.getRecordTypeName(); Long prevRecordTypeVersion = record.getRecordTypeVersion(); reset(); recordType(prevRecordTypeName, prevRecordTypeVersion); return this; } else { throw new IllegalStateException("add should only be called for a nested list of records, current mode is " + mode); } } @Override public RecordBuilder endList() { if (mode == Mode.NESTED_RECORD_LIST) { if (records == null) { records = new ArrayList<Record>(); } records.add(createRecord()); parent.field(parentField, records); return parent; } else { throw new IllegalStateException("endList should only be called when creating a nested list of records"); } } protected QName resolveNamespace(String name) { if (defaultNamespace != null) { return new QName(defaultNamespace, name); } QName recordTypeName = record.getRecordTypeName(); if (recordTypeName != null) { return new QName(recordTypeName.getNamespace(), name); } throw new IllegalStateException("Namespace could not be resolved for name '" + name + "' since no default namespace was given and no record type is set."); } }