// Copyright 2016 The Domain Registry Authors. All Rights Reserved. // // 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 google.registry.model.eppinput; import static com.google.common.collect.Sets.intersection; import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import google.registry.model.Buildable.Builder; import google.registry.model.EppResource; import google.registry.model.ImmutableObject; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.util.TypeUtils.TypeInstantiator; import java.util.List; import java.util.Set; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlTransient; /** Commands for EPP resources. */ public interface ResourceCommand { /** * A command for a single {@link EppResource}. * *

In general commands should extend {@link AbstractSingleResourceCommand} instead of * implementing this directly, but "Create" commands can't do that since they need to inherit from * a base class that gives them all of the resource's fields. The domain "Info" command also can't * do that since it's "name" field is overloaded with a "hosts" attribute. */ public interface SingleResourceCommand extends ResourceCommand { String getTargetId(); AuthInfo getAuthInfo(); } /** Abstract implementation of {@link ResourceCommand}. */ @XmlTransient public abstract static class AbstractSingleResourceCommand extends ImmutableObject implements SingleResourceCommand { @XmlElements({ @XmlElement(name = "id"), @XmlElement(name = "name") }) String targetId; @Override public String getTargetId() { return targetId; } @Override public AuthInfo getAuthInfo() { return null; } } /** A check command for an {@link EppResource}. */ @XmlTransient public static class ResourceCheck extends ImmutableObject implements ResourceCommand { @XmlElements({ @XmlElement(name = "id"), @XmlElement(name = "name") }) List targetUniqueIds; public ImmutableList getTargetIds() { return nullSafeImmutableCopy(targetUniqueIds); } } /** A create command, or the inner change (as opposed to add or remove) part of an update. */ public interface ResourceCreateOrChange> { public abstract void applyTo(B builder); } /** * An update command for an {@link EppResource}. * * @param the add-remove type * @param the change type */ @XmlTransient public abstract static class ResourceUpdate , C extends ResourceCreateOrChange> extends AbstractSingleResourceCommand { /** Part of an update command that specifies set values to add or remove. */ @XmlTransient public abstract static class AddRemove extends ImmutableObject { @XmlElement(name = "status") Set statusValues; public ImmutableSet getStatusValues() { return nullToEmptyImmutableCopy(statusValues); } } protected abstract C getNullableInnerChange(); protected abstract A getNullableInnerAdd(); protected abstract A getNullableInnerRemove(); // Don't use MoreObjects.firstNonNull in these method because it will result in an unneeded // reflective instantiation when the object isn't null. public C getInnerChange() { C change = getNullableInnerChange(); return change == null ? new TypeInstantiator(getClass()){}.instantiate() : change; } public A getInnerAdd() { A add = getNullableInnerAdd(); return add == null ? new TypeInstantiator(getClass()){}.instantiate() : add; } public A getInnerRemove() { A remove = getNullableInnerRemove(); return remove == null ? new TypeInstantiator(getClass()){}.instantiate() : remove; } public void applyTo(B builder) throws AddRemoveSameValueException { getInnerChange().applyTo(builder); if (!intersection(getInnerAdd().getStatusValues(), getInnerRemove().getStatusValues()) .isEmpty()) { throw new AddRemoveSameValueException(); } builder.addStatusValues(getInnerAdd().getStatusValues()); builder.removeStatusValues(getInnerRemove().getStatusValues()); } } /** Exception for adding and removing the same value in {@link ResourceUpdate#applyTo}. */ public static class AddRemoveSameValueException extends Exception {} }