/* GASH 2 IPDBField.java The GANYMEDE object storage system. Created: 4 Sep 1997 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2013 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Web site: http://www.arlut.utexas.edu/gash2 Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package arlut.csd.ganymede.server; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Date; import java.util.Enumeration; import java.util.Vector; import arlut.csd.ganymede.common.GanyPermissionsException; import arlut.csd.ganymede.common.IPAddress; import arlut.csd.ganymede.common.ReturnVal; import arlut.csd.ganymede.rmi.ip_field; import arlut.csd.Util.TranslationService; import arlut.csd.Util.VectorUtils; /*------------------------------------------------------------------------------ class IPDBField ------------------------------------------------------------------------------*/ /** * <p>IPDBField is a subclass of {@link * arlut.csd.ganymede.server.DBField DBField} for the storage and * handling of IPv4/IPv6 address fields in the {@link * arlut.csd.ganymede.server.DBStore DBStore} on the Ganymede * server.</p> * * <p>The Ganymede client talks to IPDBFields through the {@link * arlut.csd.ganymede.rmi.ip_field ip_field} RMI interface.</p> */ public class IPDBField extends DBField implements ip_field { /** * <p>TranslationService object for handling string localization in * the Ganymede server.</p> */ static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.ganymede.server.IPDBField"); // -- /** * <p>Receive constructor. Used to create a IPDBField from a {@link * arlut.csd.ganymede.server.DBStore DBStore}/{@link * arlut.csd.ganymede.server.DBJournal DBJournal} DataInput * stream.</p> */ IPDBField(DBObject owner, DataInput in, DBObjectBaseField definition) throws IOException { super(owner, definition.getID()); this.value = null; receive(in, definition); } /** * <p>No-value constructor. Allows the construction of a * 'non-initialized' field, for use where the * {@link arlut.csd.ganymede.server.DBObjectBase DBObjectBase} * definition indicates that a given field may be present, * but for which no value has been stored in the * {@link arlut.csd.ganymede.server.DBStore DBStore}.</p> * * <p>Used to provide the client a template for 'creating' this * field if so desired.</p> */ IPDBField(DBObject owner, DBObjectBaseField definition) { super(owner, definition.getID()); if (isVector()) { this.value = new Vector(); } else { this.value = null; } } /** * Copy constructor. */ public IPDBField(DBObject owner, IPDBField field) { super(owner, field.getID()); if (isVector()) { this.value = new Vector(field.getVectVal()); } else { this.value = field.value; } } /** * Scalar value constructor. */ public IPDBField(DBObject owner, Date value, DBObjectBaseField definition) { super(owner, definition.getID()); if (definition.isArray()) { throw new IllegalArgumentException("scalar constructor called on vector field"); } this.value = value; } /** * Vector value constructor. */ public IPDBField(DBObject owner, Vector values, DBObjectBaseField definition) { super(owner, definition.getID()); if (!definition.isArray()) { throw new IllegalArgumentException("vector constructor called on scalar field"); } if (values == null) { this.value = new Vector(); } else { this.value = new Vector(values); } } @Override public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } @Override void emit(DataOutput out) throws IOException { if (isVector()) { Vector<IPAddress> values = (Vector<IPAddress>) getVectVal(); out.writeInt(values.size()); for (IPAddress addr: values) { addr.emit(out); } } else { ((IPAddress) value).emit(out); } } @Override void receive(DataInput in, DBObjectBaseField definition) throws IOException { if (definition.isArray()) { int count; if (Ganymede.db.isLessThan(2,3)) { count = in.readShort(); } else { count = in.readInt(); } Vector<IPAddress> values = new Vector<IPAddress>(count); for (int i = 0; i < count; i++) { values.add(IPAddress.readIPAddr(in)); } this.value = values; } else { this.value = IPAddress.readIPAddr(in); } } /** * <p>This method is used when the database is being dumped, to write * out this field to disk.</p> */ @Override synchronized void emitXML(XMLDumpContext xmlOut) throws IOException { xmlOut.startElementIndent(this.getXMLName()); if (!isVector()) { emitIPXML(xmlOut, (IPAddress) this.value); } else { Vector<IPAddress> values = (Vector<IPAddress>) this.value; for (IPAddress addr: values) { xmlOut.indentOut(); xmlOut.indent(); emitIPXML(xmlOut, addr); xmlOut.indentIn(); } xmlOut.indent(); } xmlOut.endElement(this.getXMLName()); } public void emitIPXML(XMLDumpContext xmlOut, IPAddress value) throws IOException { xmlOut.startElement("ip"); xmlOut.attribute("val", value.toString()); xmlOut.endElement("ip"); } @Override public synchronized String getValueString() { if (this.value == null) { return ""; } if (!isVector()) { return ((IPAddress) this.value).toString(); } Vector<IPAddress> values = (Vector<IPAddress>) this.value; return VectorUtils.vectorString(values); } /** * The normal getValueString() encoding of IP addresses is acceptable. */ @Override public String getEncodingString() { return getValueString(); } /** * <p>Returns a String representing the change in value between this * field and orig. This String is intended for logging and email, * not for any sort of programmatic activity. The format of the * generated string is not defined, but is intended to be suitable * for inclusion in a log entry and in an email message.</p> * * <p>If there is no change in the field, null will be returned.</p> */ @Override public synchronized String getDiffString(DBField orig) { if (!(orig instanceof IPDBField)) { throw new IllegalArgumentException("bad field comparison"); } if (orig == this) { return null; } if (isVector()) { Vector added = VectorUtils.difference(getVectVal(), orig.getVectVal()); Vector deleted = VectorUtils.difference(orig.getVectVal(), getVectVal()); // were there any changes at all? if (deleted.size() == 0 && added.size() == 0) { return null; } else { StringBuilder result = new StringBuilder(); if (deleted.size() != 0) { // "\tDeleted: {0}\n" result.append(ts.l("getDiffString.deleted", VectorUtils.vectorString(deleted, ", "))); } if (added.size() != 0) { // "\tAdded: {0}\n" result.append(ts.l("getDiffString.added", VectorUtils.vectorString(added, ", "))); } return result.toString(); } } else { if (orig.value.equals(this.value)) { return null; } StringBuilder result = new StringBuilder(); // "\tOld: {0}\n" result.append(ts.l("getDiffString.old", orig.value)); // "\tNew: {0}\n" result.append(ts.l("getDiffString.new", this.value)); return result.toString(); } } // **** // // ip_field methods // // **** /** * Returns true if this field is permitted to hold IPv6 addresses. */ public boolean v6Allowed() { DBEditObject eObj = (DBEditObject) this.owner; return eObj.isIPv6OK(this); } /** * Returns true if the value stored in this IP field is an IPV4 * address. If no value has been set for this field, false is * returned. * * @see arlut.csd.ganymede.rmi.ip_field */ public boolean isIPV4() { if (isVector()) { throw new IllegalArgumentException("scalar accessor called on vector field"); } IPAddress addr = (IPAddress) this.value; return addr == null ? false : addr.isIPv4(); } /** * <p>Returns true if the value stored in the given element of this * IP field is an IPV4 address, if no value is set, this method will * return false.</p> * * @param index Array index for the value to be checked * * @see arlut.csd.ganymede.rmi.ip_field */ public boolean isIPV4(short index) { if (!isVector()) { throw new IllegalArgumentException("vector accessor called on scalar field"); } IPAddress addr = (IPAddress) getVectVal().get(index); return addr == null ? false : addr.isIPv4(); } /** * <p>Returns true if the value stored in this IP field is an IPV6 * address. If this field has no value set, this method will return * false by default.</p> * * @see arlut.csd.ganymede.rmi.ip_field */ public boolean isIPV6() { if (isVector()) { throw new IllegalArgumentException("scalar accessor called on vector field"); } IPAddress addr = (IPAddress) this.value; return addr == null ? false : addr.isIPv6(); } /** * <p>Returns true if the value stored in the given element of this * IP field is an IPV6 address. If no value is stored in this * field, false is returned.</p> * * @param index Array index for the value to be checked * * @see arlut.csd.ganymede.rmi.ip_field */ public boolean isIPV6(short index) { if (!isVector()) { throw new IllegalArgumentException("vector accessor called on scalar field"); } IPAddress addr = (IPAddress) getVectVal().get(index); return addr == null ? false : addr.isIPv6(); } // **** // // Overridable methods for implementing intelligent behavior // // **** @Override public boolean verifyTypeMatch(Object o) { return o == null || o instanceof IPAddress; } @Override public ReturnVal verifyNewValue(Object o) { DBEditObject eObj = (DBEditObject) this.owner; if (!isEditable(true)) { return Ganymede.createErrorDialog(this.getGSession(), "IP Field Error", "Don't have permission to edit field " + getName() + " in object " + eObj.getLabel()); } if (!verifyTypeMatch(o)) { return Ganymede.createErrorDialog(this.getGSession(), "IP Field Error", "Submitted value " + o + " is not a IP address! Major client error while" + " trying to edit field " + getName() + " in object " + eObj.getLabel()); } // have our parent make the final ok on the value return eObj.verifyNewValue(this, o); } /** * <p>Returns a {@link arlut.csd.ganymede.server.fieldDeltaRec * fieldDeltaRec} object listing the changes between this field's * state and that of the prior oldField state.</p> */ @Override public fieldDeltaRec getVectorDiff(DBField oldField) { if (!isVector()) { // "Vector method called on a scalar field: {1} in object {0}" throw new IllegalArgumentException(ts.l("global.oops_scalar", this.owner.getLabel(), getName())); } if (oldField == null) { // "Can''t compare fields.. oldField is null" throw new IllegalArgumentException(ts.l("getVectorDiff.null_old")); } if ((oldField.getID() != getID()) || (oldField.getObjTypeID() != getObjTypeID())) { // "Can''t compare fields.. incompatible field ids" throw new IllegalArgumentException(ts.l("getVectorDiff.bad_type")); } /* - */ fieldDeltaRec deltaRec = new fieldDeltaRec(getID()); Vector oldValues = oldField.getVectVal(); Vector newValues = getVectVal(); Vector addedValues = VectorUtils.difference(newValues, oldValues); Vector deletedValues = VectorUtils.difference(oldValues, newValues); deltaRec.addValues = addedValues; deltaRec.delValues = deletedValues; return deltaRec; } }