PMessageBuilder.java
/*
* Copyright 2015-2016 Providence Authors
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 net.morimekta.providence;
import net.morimekta.providence.descriptor.PField;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.Collection;
import static net.morimekta.util.collect.Unmodifiables.toSet;
import static net.morimekta.util.collect.Unmodifiables.toSortedSet;
/**
* Base class for message builders.
*/
public abstract class PMessageBuilder<Message extends PMessage<Message>>
implements PMessageOrBuilder<Message> {
/**
* Checks if the current set data is enough to make a valid struct. It
* will check for all required fields, and if any are missing it will
* return false.
*
* @return True for a valid message.
*/
public abstract boolean valid();
/**
* Checks if the current set data is enough to make a valid struct. It
* will check for all required fields, and if any are missing it will
* throw an {@link IllegalStateException} with an appropriate error
* message.
*
* @return The builder itself.
* @throws IllegalStateException When the builder will not generate a
* valid message model object.
*/
public abstract PMessageBuilder<Message> validate() throws IllegalStateException;
/**
* Set the provided field value.
*
* @param key The key of the field to set.
* @param value The value to be set.
* @return The message builder.
*/
@Nonnull
public abstract PMessageBuilder<Message> set(int key, Object value);
/**
* Set the provided field value.
*
* @param field The field to set.
* @param value The value to be set.
* @return The builder itself.
*/
@Nonnull
public PMessageBuilder<Message> set(@Nonnull PField<Message> field, Object value) {
return set(field.getId(), value);
}
/**
* Checks if a specific field is set on the builder.
*
* @param key The key of the field to check.
* @return True if the field is set.
*/
public abstract boolean isSet(int key);
/**
* Checks if a specific field is set on the builder.
*
* @param field The field to check.
* @return True if the field is set.
*/
public boolean isSet(@Nonnull PField<Message> field) {
return isSet(field.getId());
}
/**
* Get a Collection of F with fields set on the builder. A.k.a is
* present.
*
* Unusual naming because it avoids conflict with generated methods.
*
* @return Collection of F
*/
@Nonnull
public Collection<PField<Message>> presentFields() {
return Arrays.stream(descriptor().getFields())
.filter(this::isSet)
.collect(toSet());
}
/**
* Get a sorted set of fields set on the builder. A.k.a is present.
*
* @return Collection of F
*/
public Collection<String> presentFieldNames() {
return Arrays.stream(descriptor().getFields())
.filter(this::isSet)
.map(PField::getName)
.collect(toSortedSet());
}
/**
* Checks if a specific field is modified on the builder.
*
* @param key The key of the field to check.
* @return True if the field is modified.
*/
public abstract boolean isModified(int key);
/**
* Checks if a specific field is modified on the builder.
*
* @param field The field to check.
* @return True if the field is modified.
*/
public boolean isModified(@Nonnull PField<Message> field) {
return isModified(field.getId());
}
/**
* Get a Collection of F with fields Modified since creation of the builder.
*
* @return Collection of F
*/
@Nonnull
public Collection<PField<Message>> modifiedFields() {
return Arrays.stream(descriptor().getFields())
.filter(this::isModified)
.collect(toSet());
}
/**
* Get a sorted set of field names Modified since creation of the builder.
*
* @return Collection of F
*/
public Collection<String> modifiedFieldNames() {
return Arrays.stream(descriptor().getFields())
.filter(this::isModified)
.map(PField::getName)
.collect(toSortedSet());
}
/**
* Adds a value to a set or list container.
*
* @param key The key of the container field to add too.
* @param value The value to add.
* @return The builder itself.
* @throws IllegalArgumentException if the field is not a list or set.
*/
@Nonnull
public abstract PMessageBuilder<Message> addTo(int key, Object value);
/**
* Checks if a specific field is set on the builder.
*
* @param field The container field to add too.
* @param value The value to add.
* @return The builder itself.
*/
@Nonnull
public PMessageBuilder<Message> addTo(@Nonnull PField<Message> field, Object value) {
return addTo(field.getId(), value);
}
/**
* Clear the provided field value.
*
* @param key The key of the field to clear.
* @return The builder itself.
*/
@Nonnull
public abstract PMessageBuilder<Message> clear(int key);
/**
* Clear the provided field value.
*
* @param field The field to clear.
* @return The builder itself.
*/
@Nonnull
public PMessageBuilder<Message> clear(@Nonnull PField<Message> field) {
return clear(field.getId());
}
/**
* Merges the provided message into the builder. Contained messages should
* in turn be merged and not replaced wholesale. Sets are unioned (addAll)
* and maps will overwrite / replace on a per-key basis (putAll).
*
* @param from The message to merge values from.
* @return The builder itself.
*/
@Nonnull
public abstract PMessageBuilder<Message> merge(@Nonnull Message from);
/**
* Get the builder for the given message contained in this builder. If
* the sub-builder does not exist, create, either from existing instance
* or from scratch.
*
* @param key The field key.
* @return The field builder.
* @throws IllegalArgumentException if field is not a message field.
*/
@Nonnull
public abstract PMessageBuilder<?> mutator(int key);
/**
* Get the builder for the given message contained in this builder. If
* the sub-builder does not exist, create, either from existing instance
* or from scratch.
*
* @param field The field to mutate.
* @return The field builder.
* @throws IllegalArgumentException if field is not a message field.
*/
@Nonnull
public PMessageBuilder<?> mutator(@Nonnull PField<Message> field) {
return mutator(field.getId());
}
@Nonnull
public abstract Message build();
}