ProtoMessageBuilder.java
/*
* Copyright 2022 Proto Utils 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.proto;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Message;
import net.morimekta.proto.utils.ValueUtil;
import java.util.Objects;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
import static net.morimekta.proto.utils.FieldUtil.getDefaultFieldValue;
import static net.morimekta.proto.utils.FieldUtil.getDefaultTypeValue;
/**
* A wrapper around a proto message builder to behave more like it's actual builder
* with methods reflecting both getters and setters.
*/
public class ProtoMessageBuilder
extends ProtoMessageOrBuilder {
/**
* @param builder Message builder to wrap.
*/
public ProtoMessageBuilder(Message.Builder builder) {
super(builder);
}
/**
* @param descriptor Message descriptor to get builder for.
*/
public ProtoMessageBuilder(Descriptors.Descriptor descriptor) {
this(ProtoMessage.newBuilder(descriptor));
}
/**
* @param type Message type to get builder for.
*/
public ProtoMessageBuilder(Class<?> type) {
this(ProtoMessage.newBuilder(type));
}
@Override
public Message.Builder getMessage() {
return (Message.Builder) super.getMessage();
}
@Override
public <T> T get(Descriptors.FieldDescriptor field) {
requireNonNull(field, "field == null");
return getInternal(field);
}
@Override
public <T> Optional<T> optional(Descriptors.FieldDescriptor field) {
requireNonNull(field, "field == null");
return optionalInternal(field);
}
/**
* Set the field value.
*
* @param field The field to set.
* @param value The java vale for the field. Or null to clear value.
*/
public void set(Descriptors.FieldDescriptor field, Object value) {
requireNonNull(field, "field == null");
if (value == null) {
getMessage().clearField(field);
} else {
getMessage().setField(field, ValueUtil.toProtoValue(field, value));
}
}
/**
* Get the mutable instance for the field.
*
* @param field The field to get mutating type for.
* @param <T> The field type.
* @return The mutating value, or the value if no mutating value exists.
*/
@SuppressWarnings("unchecked")
public <T> T mutable(Descriptors.FieldDescriptor field) {
requireNonNull(field, "field == null");
if (field.isRepeated()) {
if (field.isMapField()) {
return (T) new ProtoMapBuilder<>(getMessage(), field);
} else {
return (T) new ProtoListBuilder<>(getMessage(), field);
}
} else if (field.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
return (T) getMessage().getFieldBuilder(field);
} else if (hasInternal(field)) {
// not changed.
return getInternal(field);
} else {
var defaultValue = getDefaultFieldValue(field);
var defaultForType = getDefaultTypeValue(field);
if (!Objects.equals(defaultForType, defaultValue)) {
// only matters for proto v2.
set(field, defaultValue);
}
return (T) defaultValue;
}
}
/**
* @param field Clear field on message.
*/
public void clear(Descriptors.FieldDescriptor field) {
requireNonNull(field, "field == null");
getMessage().clearField(field);
}
}