All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kohsuke.github.AbstractBuilder Maven / Gradle / Ivy

The newest version!
package org.kohsuke.github;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import java.io.IOException;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/**
 * An abstract data object builder/updater.
 *
 * This class can be use to make a Builder that supports both batch and single property changes.
 * 

* Batching looks like this: *

* *
 * update().someName(value).otherName(value).done()
 * 
*

* Single changes look like this: *

* *
 * set().someName(value);
 * set().otherName(value);
 * 
*

* If {@link S} is the same as {@link R}, {@link #with(String, Object)} will commit changes after the first value change * and return a {@link R} from {@link #done()}. *

*

* If {@link S} is not the same as {@link R}, {@link #with(String, Object)} will batch together multiple changes and let * the user call {@link #done()} when they are ready. * * @author Liam Newman * @param * Final return type built by this builder returned when {@link #done()}} is called. * @param * Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If {@link S} * the same as {@link R}, this builder will commit changes after each call to {@link #with(String, Object)}. */ abstract class AbstractBuilder extends GitHubInteractiveObject implements GitHubRequestBuilderDone { @Nonnull private final Class returnType; private final boolean commitChangesImmediately; @CheckForNull private final R baseInstance; /** The requester. */ @Nonnull protected final Requester requester; // TODO: Not sure how update-in-place behavior should be controlled // However, it certainly can be controlled dynamically down to the instance level or inherited for all children of // some connection. /** The update in place. */ protected boolean updateInPlace; /** * Creates a builder. * * @param finalReturnType * the final return type for built by this builder returned when {@link #done()}} is called. * @param intermediateReturnType * the intermediate return type of type {@link S} returned by calls to {@link #with(String, Object)}. * Must either be equal to {@code builtReturnType} or this instance must be castable to this class. If * not, the constructor will throw {@link IllegalArgumentException}. * @param root * the GitHub instance to connect to. * @param baseInstance * optional instance on which to base this builder. */ @SuppressFBWarnings(value = { "CT_CONSTRUCTOR_THROW" }, justification = "argument validation, internal class") protected AbstractBuilder(@Nonnull Class finalReturnType, @Nonnull Class intermediateReturnType, @Nonnull GitHub root, @CheckForNull R baseInstance) { super(root); this.requester = root.createRequest(); this.returnType = finalReturnType; this.commitChangesImmediately = returnType.equals(intermediateReturnType); if (!commitChangesImmediately && !intermediateReturnType.isInstance(this)) { throw new IllegalArgumentException( "Argument \"intermediateReturnType\": This instance must be castable to intermediateReturnType or finalReturnType must be equal to intermediateReturnType."); } this.baseInstance = baseInstance; this.updateInPlace = false; } /** * {@inheritDoc} */ @Override @Nonnull @BetaApi public R done() throws IOException { R result; if (updateInPlace && baseInstance != null) { result = requester.fetchInto(baseInstance); } else { result = requester.fetch(returnType); } return result; } /** * Applies a value to a name for this builder. * * If {@link S} is the same as {@link R}, this method will commit changes after the first value change and return a * {@link R} from {@link #done()}. * * If {@link S} is not the same as {@link R}, this method will return an {@link S} and letting the caller batch * together multiple changes and call {@link #done()} when they are ready. * * @param name * the name of the field * @param value * the value of the field * @return either a continuing builder or an updated data record * @throws IOException * if an I/O error occurs */ @Nonnull @BetaApi protected S with(@Nonnull String name, Object value) throws IOException { requester.with(name, value); return continueOrDone(); } /** * Chooses whether to return a continuing builder or an updated data record * * If {@link S} is the same as {@link R}, this method will commit changes after the first value change and return a * {@link R} from {@link #done()}. * * If {@link S} is not the same as {@link R}, this method will return an {@link S} and letting the caller batch * together multiple changes and call {@link #done()} when they are ready. * * @return either a continuing builder or an updated data record * @throws IOException * if an I/O error occurs */ @Nonnull @BetaApi protected S continueOrDone() throws IOException { // This little bit of roughness in this base class means all inheriting builders get to create Updater and // Setter classes from almost identical code. Creator can often be implemented with significant code reuse as // well. if (commitChangesImmediately) { // These casts look strange and risky, but they they're actually guaranteed safe due to the return path // being based on the previous comparison of class instances passed to the constructor. return (S) done(); } else { return (S) this; } } }