/* * 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 org.apache.openjpa.trader.domain; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.MappedSuperclass; import javax.persistence.OneToOne; import javax.persistence.Version; /** * An abstract root for domain objects in OpenTrader designates a {@link Stock financial instrument} * that can be traded. An abstract state of a tradable entity is immutable by the application. * * @author Pinaki Poddar * */ @SuppressWarnings("serial") @MappedSuperclass public abstract class Tradable implements Serializable { /** * Primary identity of a traded entity. * Its value is generated by the persistence provider. * The application must not set or change this value. * Hence no setter method is provided. */ @Id @GeneratedValue private Long id; /** * The price at which the underlying instrument be traded. * Must always be positive. */ @Column(precision=10,scale=2) private double price; /** * The volume or discreet number of underlying instrument be traded. * Must always be positive. */ private int volume; /** * The underlying instrument. * Must never be null. */ @ManyToOne(cascade={CascadeType.MERGE,CascadeType.DETACH,CascadeType.REFRESH},optional=false) private Stock stock; @OneToOne(cascade={CascadeType.MERGE,CascadeType.DETACH, CascadeType.REFRESH}, optional=true, fetch=FetchType.LAZY) private Trade trade; /** * A version identifier. * Important (and almost mandatory) for any persistent entity to be part of * simultaneous transaction. The persistence provider uses/updates the * version to detect concurrent modification of an entity in simultaneous * transaction. */ @Version private int version; /** * A no-arg constructor to comply to both GWT compiler and OpenJPA * bytecode enhancer. */ protected Tradable() { } /** * Real constructor to be used by the concrete derivations. * * @param stock the underlying instrument. Must not be null. * @param price the price. Must be positive. * @param volume the volume. Must be positive. */ protected Tradable(Stock stock, double price, int volume) { if (stock == null) throw new IllegalArgumentException("Can not create Tradable with null stock"); if (price <= 0.0) throw new IllegalArgumentException("Can not create Tradable with non-positive price " + price); if (volume <= 0) throw new IllegalArgumentException("Can not create Tradable with non-positive volume " + volume); this.stock = stock; this.price = price; this.volume = volume; } /** * Gets the identifier of this entity. * * @return identifier generated by the persistence provider. */ public long getId() { return id; } /** * Gets the underlying instrument. * * @return the underlying instrument. Never null. */ public Stock getStock() { return stock; } /** * Gets the price at which the underlying instrument be traded. * * @return the price of the underlying instrument for trading. Always greater than zero. */ public double getPrice() { return price; } /** * Gets the volume of the underlying instrument be traded. * * @return the volume of the underlying instrument for trading. Always greater than zero. */ public int getVolume() { return volume; } public abstract double getGain(); /** * Affirms if this offer has expired. */ public boolean isTraded() { return trade != null; } public void updateStock(Stock updated) { if (this.stock.equals(updated)) { this.stock = updated; } else { throw new IllegalArgumentException(this + " can not change Stock from " + this.stock + " to " + updated); } } public void setTrade(Trade t) { if (trade != null) throw new IllegalStateException(this + " has already been traded"); this.trade = t; } public Trade getTrade() { return trade; } /** * The version of the entity. Updated by the persistence provider. * * @return the current version of this entity. */ public int getVersion() { return version; } /** * It is important for persistence entity to overwrite the equals() * method, preferably based only on its primary key attribute(s). */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } /** * It is important for persistence entity to overwrite the hashCode() * method, preferably based only on its primary key attribute(s). */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Tradable other = (Tradable) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } public String toString() { String typeName = getClass().getName(); return typeName.substring(typeName.lastIndexOf('.')+1) + "-" + id; } }