EmptyModel.java

/*
 * Copyright 2020 Global Crop Diversity Trust
 *
 * 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 org.genesys.blocks.model;

import java.io.Serializable;
import java.util.Objects;

import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;

import org.genesys.blocks.util.JsonClassNameWriter;
import org.springframework.data.domain.Persistable;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonAppend;

import lombok.Getter;
import lombok.Setter;

/**
 * {@code Emptymodel} is the most basic entity type supported by app-blocks.
 */
@Getter
@Setter
@MappedSuperclass
@JsonAppend(props = { @JsonAppend.Prop(name = "_class", value = JsonClassNameWriter.class, type = String.class) })
public abstract class EmptyModel implements EntityId, Serializable, Persistable<Long> {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 5934941826741972456L;

	/**
	 * Is the entity persisted to the database? Test is based on value of
	 * {@link #getId()} field where negative integers or null mean entity is not yet
	 * persisted.
	 * 
	 * <b>Don't override.</b>
	 *
	 * @return true if the {@link #getId()} is a positive integer, false otherwise
	 */
	@JsonIgnore
	@Transient
	public final boolean isNew() {
		var id = getId();
		return id == null || id < 0;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		var id = getId();
		result = prime * (id == null || id < 0 ? -3 : id.hashCode());
		return result;
	}

	/**
	 * Most entities should not override this method!
	 * 
	 * If you do, make sure to also implement {@link #hashCode()} 
	 */
	@Override
	public boolean equals(final Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof EmptyModel)) {
			return false;
		}

		final EmptyModel other = (EmptyModel) obj;
//			if (getClass() != obj.getClass()) return false; // Doesn't handle Hibernate proxies (proxy class is not equal to entity class)
//			if (! getClass().isAssignableFrom(obj.getClass())) return false; // Doesn't handle Hibernate proxies (proxy can be assigned to entity, but entity can't be assigned to proxy)
		if (! other.canEqual(this)) { // This is to ensure that Hibernate inheritance types don't match eachother
			return false;
		}
		var thisId = this.getId();
		var otherId = other.getId();
		if (thisId == null || otherId == null || thisId < 0 || otherId < 0) {
			// Two models are never equal if they are new
			return false;
		} else if (! Objects.equals(thisId, otherId)) {
			return false;
		}
		return true;
	}

	/**
	 * Implement your own {@code return (other instanceof X)}.
	 *
	 * @param other the other object
	 * @return {@code true} if {@code other} is an instance of the same type as {@code this}
	 */
	public abstract boolean canEqual(Object other);

}