org.kohsuke.github.example.dataobject.ReadOnlyObjects Maven / Gradle / Ivy
package org.kohsuke.github.example.dataobject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nonnull;
/**
* {@link org.kohsuke.github.GHMeta} wraps the list of GitHub's IP addresses.
*
* This class is used to show examples of different ways to create simple read-only data objects. For data objects that
* can be modified, perform actions, or get other objects we'll need other examples.
*
* IMPORTANT: There is no one right way to do this, but there are better and worse.
*
* - Better: {@link GHMetaGettersUnmodifiable} is a good balance of clarity and brevity
* - Worse: {@link GHMetaPublic} exposes setters that are not needed, making it unclear that fields are actually
* read-only
*
*
* @author Liam Newman
* @see org.kohsuke.github.GHMeta
* @see Get Meta
*/
public final class ReadOnlyObjects {
/**
* All GHMeta data objects should expose these values.
*
* @author Liam Newman
*/
public interface GHMetaExample {
/**
* Is verifiable password authentication boolean.
*
* @return the boolean
*/
boolean isVerifiablePasswordAuthentication();
/**
* Gets hooks.
*
* @return the hooks
*/
List getHooks();
/**
* Gets git.
*
* @return the git
*/
List getGit();
/**
* Gets web.
*
* @return the web
*/
List getWeb();
/**
* Gets api.
*
* @return the api
*/
List getApi();
/**
* Gets pages.
*
* @return the pages
*/
List getPages();
/**
* Gets importer.
*
* @return the importer
*/
List getImporter();
}
/**
* This version uses public getters and setters and leaves it up to Jackson how it wants to fill them.
*
* Pro:
*
* - Easy to create
* - Not much code
* - Minimal annotations
*
* Con:
*
* - Exposes public setters for fields that should not be changed, flagged by spotbugs
* - Lists modifiable when they should not be changed
* - Jackson generally doesn't call the setters, it just sets the fields directly
*
*
* @author Paulo Miguel Almeida
* @see org.kohsuke.github.GHMeta
*/
public static class GHMetaPublic implements GHMetaExample {
@JsonProperty("verifiable_password_authentication")
private boolean verifiablePasswordAuthentication;
private List hooks;
private List git;
private List web;
private List api;
private List pages;
private List importer;
public boolean isVerifiablePasswordAuthentication() {
return verifiablePasswordAuthentication;
}
/**
* Sets verifiable password authentication.
*
* @param verifiablePasswordAuthentication
* the verifiable password authentication
*/
public void setVerifiablePasswordAuthentication(boolean verifiablePasswordAuthentication) {
this.verifiablePasswordAuthentication = verifiablePasswordAuthentication;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getHooks() {
return hooks;
}
/**
* Sets hooks.
*
* @param hooks
* the hooks
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP2" }, justification = "Spotbugs also doesn't like this")
public void setHooks(List hooks) {
this.hooks = hooks;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getGit() {
return git;
}
/**
* Sets git.
*
* @param git
* the git
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP2" }, justification = "Spotbugs also doesn't like this")
public void setGit(List git) {
this.git = git;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getWeb() {
return web;
}
/**
* Sets web.
*
* @param web
* the web
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP2" }, justification = "Spotbugs also doesn't like this")
public void setWeb(List web) {
this.web = web;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getApi() {
return api;
}
/**
* Sets api.
*
* @param api
* the api
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP2" }, justification = "Spotbugs also doesn't like this")
public void setApi(List api) {
this.api = api;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getPages() {
return pages;
}
/**
* Sets pages.
*
* @param pages
* the pages
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP2" }, justification = "Spotbugs also doesn't like this")
public void setPages(List pages) {
this.pages = pages;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getImporter() {
return importer;
}
/**
* Sets importer.
*
* @param importer
* the importer
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP2" }, justification = "Spotbugs also doesn't like this")
public void setImporter(List importer) {
this.importer = importer;
}
}
/**
* This version uses public getters and shows that package or private setters both can be used by jackson. You can
* check this by running in debug and setting break points in the setters.
*
*
* Pro:
*
* - Easy to create
* - Not much code
* - Some annotations
*
* Con:
*
* - Exposes some package setters for fields that should not be changed, better than public
* - Lists modifiable when they should not be changed
*
*
* @author Liam Newman
* @see org.kohsuke.github.GHMeta
*/
public static class GHMetaPackage implements GHMetaExample {
private boolean verifiablePasswordAuthentication;
private List hooks;
private List git;
private List web;
private List api;
private List pages;
/**
* Missing {@link JsonProperty} or having it on the field will cause Jackson to ignore getters and setters.
*/
@JsonProperty
private List importer;
@JsonProperty("verifiable_password_authentication")
public boolean isVerifiablePasswordAuthentication() {
return verifiablePasswordAuthentication;
}
private void setVerifiablePasswordAuthentication(boolean verifiablePasswordAuthentication) {
this.verifiablePasswordAuthentication = verifiablePasswordAuthentication;
}
@JsonProperty
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getHooks() {
return hooks;
}
/**
* Setters can be private (or package local) and will still be called by Jackson. The {@link JsonProperty} can
* got on the getter or setter and still work.
*
* @param hooks
* list of hooks
*/
private void setHooks(List hooks) {
this.hooks = hooks;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getGit() {
return git;
}
/**
* Since we mostly use Jackson for deserialization, {@link JsonSetter} is also okay, but {@link JsonProperty} is
* preferred.
*
* @param git
* list of git addresses
*/
@JsonSetter
void setGit(List git) {
this.git = git;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getWeb() {
return web;
}
/**
* The {@link JsonProperty} can got on the getter or setter and still work.
*
* @param web
* list of web addresses
*/
void setWeb(List web) {
this.web = web;
}
@JsonProperty
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getApi() {
return api;
}
void setApi(List api) {
this.api = api;
}
@JsonProperty
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getPages() {
return pages;
}
void setPages(List pages) {
this.pages = pages;
}
/**
* Missing {@link JsonProperty} or having it on the field will cause Jackson to ignore getters and setters.
*
* @return list of importer addresses
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Noted above")
public List getImporter() {
return importer;
}
/**
* Missing {@link JsonProperty} or having it on the field will cause Jackson to ignore getters and setters.
*
* @param importer
* list of importer addresses
*/
void setImporter(List importer) {
this.importer = importer;
}
}
/**
* This version uses only public getters and returns unmodifiable lists.
*
*
*
* Pro:
*
* - Very Easy to create
* - Minimal code
* - Minimal annotations
* - Fields effectively final and lists unmodifiable
*
* Con:
*
* - Effectively final is not quite really final
* - If one of the lists were missing (an option member, for example), it will throw NPE but we could mitigate by
* checking for null or assigning a default.
*
*
* @author Liam Newman
* @see org.kohsuke.github.GHMeta
*/
public static class GHMetaGettersUnmodifiable implements GHMetaExample {
@JsonProperty("verifiable_password_authentication")
private boolean verifiablePasswordAuthentication;
private List hooks;
private List git;
private List web;
private List api;
private List pages;
/**
* If this were an optional member, we could fill it with an empty list by default.
*/
private List importer = new ArrayList<>();
public boolean isVerifiablePasswordAuthentication() {
return verifiablePasswordAuthentication;
}
public List getHooks() {
return Collections.unmodifiableList(hooks);
}
public List getGit() {
return Collections.unmodifiableList(git);
}
public List getWeb() {
return Collections.unmodifiableList(web);
}
public List getApi() {
return Collections.unmodifiableList(api);
}
public List getPages() {
return Collections.unmodifiableList(pages);
}
public List getImporter() {
return Collections.unmodifiableList(importer);
}
}
/**
* This version uses only public getters and returns unmodifiable lists and has final fields
*
* Pro:
*
* - Moderate amount of code
* - More annotations
* - Fields final and lists unmodifiable
*
* Con:
*
* - Extra allocations - default array lists will be replaced by Jackson (yes, even though they are final)
* - Added constructor is annoying
* - If this object could be refreshed or populated, then the final is misleading (and possibly buggy)
*
*
* @author Liam Newman
* @see org.kohsuke.github.GHMeta
*/
public static class GHMetaGettersFinal implements GHMetaExample {
private final boolean verifiablePasswordAuthentication;
private final List hooks = new ArrayList<>();
private final List git = new ArrayList<>();
private final List web = new ArrayList<>();
private final List api = new ArrayList<>();
private final List pages = new ArrayList<>();
private final List importer = new ArrayList<>();
@JsonCreator
private GHMetaGettersFinal(
@JsonProperty("verifiable_password_authentication") boolean verifiablePasswordAuthentication) {
// boolean fields when final seem to be really final, so we have to switch to constructor
this.verifiablePasswordAuthentication = verifiablePasswordAuthentication;
}
public boolean isVerifiablePasswordAuthentication() {
return verifiablePasswordAuthentication;
}
public List getHooks() {
return Collections.unmodifiableList(hooks);
}
public List getGit() {
return Collections.unmodifiableList(git);
}
public List getWeb() {
return Collections.unmodifiableList(web);
}
public List getApi() {
return Collections.unmodifiableList(api);
}
public List getPages() {
return Collections.unmodifiableList(pages);
}
public List getImporter() {
return Collections.unmodifiableList(importer);
}
}
/**
* This version uses only public getters and returns unmodifiable lists
*
* Pro:
*
* - Fields final and lists unmodifiable
* - Construction behavior can be controlled - if values depended on each other or needed to be set in a specific
* order, this could do that.
* - JsonProrperty "required" works on JsonCreator constructors - lets annotation define required values
*
* Con:
*
* - There is no way you'd know about this without some research
* - Specific annotations needed
* - Nonnull annotations are misleading - null value is not checked even for "required" constructor
* parameters
* - Brittle and verbose - not friendly to large number of fields
*
*
* @author Liam Newman
* @see org.kohsuke.github.GHMeta
*/
public static class GHMetaGettersFinalCreator implements GHMetaExample {
private final boolean verifiablePasswordAuthentication;
private final List hooks;
private final List git;
private final List web;
private final List api;
private final List pages;
private final List importer;
/**
*
* @param hooks
* the hooks - required property works, but only on creator json properties like this, ignores
* Nonnull, checked manually
* @param git
* the git list - required property works, but only on creator json properties like this, misleading
* Nonnull annotation
* @param web
* the web list - misleading Nonnull annotation
* @param api
* the api list - misleading Nonnull annotation
* @param pages
* the pages list - misleading Nonnull annotation
* @param importer
* the importer list - misleading Nonnull annotation
* @param verifiablePasswordAuthentication
* true or false
*/
@JsonCreator
private GHMetaGettersFinalCreator(@Nonnull @JsonProperty(value = "hooks", required = true) List hooks,
@Nonnull @JsonProperty(value = "git", required = true) List git,
@Nonnull @JsonProperty("web") List web,
@Nonnull @JsonProperty("api") List api,
@Nonnull @JsonProperty("pages") List pages,
@Nonnull @JsonProperty("importer") List importer,
@JsonProperty("verifiable_password_authentication") boolean verifiablePasswordAuthentication) {
// to ensure a value is actually not null we still have to do a null check
Objects.requireNonNull(hooks);
this.verifiablePasswordAuthentication = verifiablePasswordAuthentication;
this.hooks = Collections.unmodifiableList(hooks);
this.git = Collections.unmodifiableList(git);
this.web = Collections.unmodifiableList(web);
this.api = Collections.unmodifiableList(api);
this.pages = Collections.unmodifiableList(pages);
this.importer = Collections.unmodifiableList(importer);
}
public boolean isVerifiablePasswordAuthentication() {
return verifiablePasswordAuthentication;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Unmodifiable but spotbugs doesn't detect")
public List getHooks() {
return hooks;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Unmodifiable but spotbugs doesn't detect")
public List getGit() {
return git;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Unmodifiable but spotbugs doesn't detect")
public List getWeb() {
return web;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Unmodifiable but spotbugs doesn't detect")
public List getApi() {
return api;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Unmodifiable but spotbugs doesn't detect")
public List getPages() {
return pages;
}
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Unmodifiable but spotbugs doesn't detect")
public List getImporter() {
return importer;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy