/*
GASH 2
DBObjectBaseField.java
The GANYMEDE object storage system.
Created: 27 August 1996
Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu
-----------------------------------------------------------------------
Ganymede Directory Management System
Copyright (C) 1996-2014
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.io.PrintWriter;
import java.rmi.RemoteException;
import java.util.Date;
import java.util.Enumeration;
import arlut.csd.JDialog.JDialogBuff;
import arlut.csd.Util.StringUtils;
import arlut.csd.Util.TranslationService;
import arlut.csd.Util.XMLItem;
import arlut.csd.Util.XMLNameValidator;
import arlut.csd.Util.XMLUtils;
import arlut.csd.ganymede.common.FieldTemplate;
import arlut.csd.ganymede.common.FieldType;
import arlut.csd.ganymede.common.ReturnVal;
import arlut.csd.ganymede.common.SchemaConstants;
import arlut.csd.ganymede.rmi.Base;
import arlut.csd.ganymede.rmi.BaseField;
/*------------------------------------------------------------------------------
class
DBObjectBaseField
------------------------------------------------------------------------------*/
/**
* <p>An entry in the Ganymede server's {@link
* arlut.csd.ganymede.server.DBStore DBStore} schema dictionary.
* DBStore contains a collection of {@link
* arlut.csd.ganymede.server.DBObjectBase DBObjectBase} objects, which
* define the schema information for a particular type of object held
* in the Ganymede database. A DBObjectBaseField is contained within
* a DBObjectBase, and defines the name, id, type, and constraints of
* a particular field that can be held in {@link
* arlut.csd.ganymede.server.DBObject DBObjects} of that type,
* including a controlling {@link
* arlut.csd.ganymede.server.DBNameSpace DBNameSpace}, if
* appropriate.</p>
*
* <p>Each {@link arlut.csd.ganymede.server.DBField DBField} held in
* the server's database knows how to lookup its controlling
* DBObjectBaseField, and the DBField's methods will consult the
* DBObjectBaseField during run-time to make decisions based on
* specified constraints defined in the DBObjectBaseField.</p>
*
* <p>The Ganymede schema editor uses the {@link
* arlut.csd.ganymede.rmi.BaseField BaseField} remote interface to
* make changes to a DBObjectBaseField's constraint information during
* schema editing. The Ganymede client may also use the BaseField
* interface to learn about the field's type information, but it may
* also download a {@link arlut.csd.ganymede.common.FieldTemplate
* FieldTemplate} that carries a DBObjectBaseField's type information
* in an efficiently retrieved summary.</p>
*/
public final class DBObjectBaseField implements BaseField, FieldType, Comparable {
static final boolean debug = false;
/**
* <p>TranslationService object for handling string localization in
* the Ganymede server.</p>
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.ganymede.server.DBObjectBaseField");
static final ReturnVal warning1 = genWarning1();
static final ReturnVal warning2 = genWarning2();
/**
* Object type definition for the database object class we are member of
*/
private final DBObjectBase base;
/**
* name of this field
*/
private String field_name = null;
/**
* id of this field in the current object type
*/
private short field_code = -1;
/**
* {@link arlut.csd.ganymede.common.FieldType Field Type} for this field
*/
private short field_type = -1;
/**
* Should this field be displayed to the client? May be false for
* some fields intended for 'scratch-pad' use, as in serving as
* anchors for compound namespace use. (i.e., the case where two
* fields in an object are considered together for namespace use..
* in this case, a hidden field might be defined with custom code
* updating the hidden field whenever either of the two visible
* fields are changed. The hidden field will have a value of
* XY where X is the contents of field 1 and Y the contents of
* field 2. Oh, never mind.)
*/
private boolean visibility = true;
/**
* string to be displayed in the client as a tooltip explaining this field
*/
private String comment = null;
/**
* name of the tab this field should be placed in on the client
*/
private String tabName = null;
/**
* true if this field is an array type
*/
private boolean array = false;
// array attributes
/**
* max length of array
*/
private short limit = Short.MAX_VALUE;
// boolean attributes
private boolean labeled = false;
private String trueLabel = null;
private String falseLabel = null;
// string attributes
private short minLength = 0;
private short maxLength = Short.MAX_VALUE;
private String okChars = null;
private String badChars = null;
private DBNameSpace namespace = null;
private boolean multiLine = false;
/**
* Regular Expression string for input-filtering
* in {@link arlut.csd.ganymede.server.StringDBField}s.
*/
private String regexpPat = null; // introduced in ganymede.db version 1.14
/**
* Text description of the meaning of the regexpPat,
* if defined
*/
private String regexpDesc = null;
/**
* Compiled regular expression for input-filtering
* in {@link arlut.csd.ganymede.server.StringDBField}s.
*/
private java.util.regex.Pattern regexp = null;
// invid attributes
private boolean editInPlace = false;
private short allowedTarget = -1; // no target restrictions
private short targetField = -1; // no field symmetry.. we use the DBStore backPointers structure by default
/**
* If this is not null, then we have gotten information on this
* Invid DBObjectBaseField pointing to a type of object from an XML
* file, and we'll need to do type resolution once the schema is
* completely loaded from an XML stream. Once this happens,
* allowedTarget will be set properly, and allowedTargetStr will be
* set to null.
*/
private String allowedTargetStr = null;
/**
* If this is not null, then we have gotten information on this
* Invid DBObjectBaseField linked to a field from an XML file, and
* we'll need to do type resolution once the schema is completely
* loaded from an XML stream. Once this happens, targetField will
* be set properly, and targetFieldStr will be set to null.
*/
private String targetFieldStr = null;
// password attributes
/**
* If true, we'll check passwords entered into this field against cracklib.
*/
private boolean cracklib_check = false;
/**
* If true, supergash will be allowed to get away with entering
* passwords into this field that do not pass cracklib validation.
*/
private boolean cracklib_supergash_exception = false;
/**
* If true, passwords will be checked against previous passwords for
* this object.
*/
private boolean history_check = false;
/**
* If true, supergash will be allowed to get away with entering
* passwords into this field that were previously used for this
* object.
*/
private boolean history_supergash_exception = false;
/**
* How many previous password hashes will be kept to check against
* newly entered passwords, if history_check is true.
*/
private int history_depth = 0;
private boolean crypted = true; // UNIX encryption is the default.
private boolean md5crypted = false; // OpenBSD style md5crypt() is not
private boolean apachemd5crypted = false; // Apache style md5crypt() is not
private boolean winHashed = false; // Windows NT/Samba hashes are not
private boolean sshaHashed = false; // SSHA hash is not either
private boolean bCrypted = false; // OpenBSD BCrypt is not either
private boolean shaUnixCrypted = false; // SHA Unix Crypt is not either
private boolean storePlaintext = false; // nor is plaintext
/**
* If the password field is to use the bCrypt algorithm, we'll need
* to know how many rounds to use. This value holds the exponent of
* the round count.
*/
private int bCryptRounds = 10;
/**
* If the password field is to use the shaUnixCrypt algorithm, which
* variant shall we use? If useShaUnixCrypted512 is true, we'll use
* the SHA512 version, if it is false, we'll use the SHA256 version.
*/
private boolean useShaUnixCrypted512 = false;
/**
* If the password field is to use the shaUnixCrypt algorithm, how
* many rounds shall we specify?
*/
private int shaUnixCryptRounds = 5000;
// schema editing
/**
* If we are being edited, this will point to an instance
* of a server-side schema editing class. */
private DBSchemaEdit editor;
/**
* Downloadable FieldTemplate representing the constant field type
* attributes represented by this DBObjectBaseField. This template
* is regenerated whenever clearEditor() is called, upon schema
* editing completion.
*/
private FieldTemplate template;
/**
* <p>A three state flag used by isInUse() to report whether or
* not a particular field is in use in the loaded database.</p>
*/
private Boolean inUseCache = null;
/**
* <p>Timestamp for the last time a field of this type was changed
* in a transaction, across all {@link
* arlut.csd.ganymede.server.DBObject DBObjects} in the {@link
* arlut.csd.ganymede.server.DBObjectBase DBObjectBase} that contains this
* field definition.</p>
*
* <p>Used to allow subclasses of {@link
* arlut.csd.ganymede.server.GanymedeBuilderTask
* GanymedeBuilderTask} to decide whether they want to trigger a
* particular build or sub-build.</p>
*/
private Date lastChange;
/**
* This field is used to handle field order sorting when
* we read an old (pre-2.0) ganymede.db file.
*
* Note that this is currently DBObjectBaseField's only package
* private field, because we share the use of these tmp_displayOrder
* fields with DBBaseCategory and DBObjectBase, and there's just
* little upside in reworking the old compatibility code to use
* setters and accessors for this purpose.
*/
int tmp_displayOrder = -1;
/* -- */
/**
* Generic field constructor.
*/
DBObjectBaseField(DBObjectBase base) throws RemoteException
{
this.base = base;
this.editor = base.getEditor();
field_name = "";
comment = "";
tabName = ts.l("receive.default_tab_name"); // "General"
field_code = -1;
field_type = -1;
lastChange = new Date();
Ganymede.rmi.publishObject(this);
}
/**
* Receive constructor, for binary loading from ganymede.db.
*/
DBObjectBaseField(DataInput in, DBObjectBase base) throws IOException, RemoteException
{
this(base);
receive(in);
template = new FieldTemplate(this);
}
/**
* <p>Copy constructor, used during schema editing.</p>
*
* <p><b>IMPORTANT: BE SURE TO ALWAYS EDIT THIS METHOD IF YOU ADD
* ANY FIELDS TO THIS CLASS!</b></p>
*/
DBObjectBaseField(DBObjectBaseField original, DBObjectBase newBase) throws RemoteException
{
this(newBase);
// Note that this method does direct property access for all
// copying, rather than using our setters, thereby bypassing the
// state checks for permissions, etc.
field_name = original.field_name; // name of this field
field_code = original.field_code; // id of this field in the current object
field_type = original.field_type; // data type contained herein
visibility = original.visibility;
comment = original.comment;
array = original.array; // true if this field is an array type
limit = original.limit;
tabName = original.tabName;
labeled = original.labeled;
trueLabel = original.trueLabel;
falseLabel = original.falseLabel;
minLength = original.minLength;
maxLength = original.maxLength;
okChars = original.okChars;
badChars = original.badChars;
namespace = original.namespace; // we point to the original namespace.. not a problem, since they are immutable
multiLine = original.multiLine;
regexpPat = original.regexpPat;
regexpDesc = original.regexpDesc;
regexp = original.regexp;
editInPlace = original.editInPlace;
allowedTarget = original.allowedTarget;
targetField = original.targetField;
cracklib_check = original.cracklib_check;
cracklib_supergash_exception = original.cracklib_supergash_exception;
history_check = original.history_check;
history_supergash_exception = original.history_supergash_exception;
history_depth = original.history_depth;
crypted = original.crypted;
md5crypted = original.md5crypted;
apachemd5crypted = original.apachemd5crypted;
winHashed = original.winHashed;
sshaHashed = original.sshaHashed;
bCryptRounds = original.bCryptRounds;
shaUnixCrypted = original.shaUnixCrypted;
useShaUnixCrypted512 = original.useShaUnixCrypted512;
shaUnixCryptRounds = original.shaUnixCryptRounds;
storePlaintext = original.storePlaintext;
inUseCache = null;
// We'll just re-use the original's FieldTemplate for the time
// being.. when the SchemaEditor is done, it will call
// clearEditor() on our DBObjectBase, which will create a new
// FieldTemplate for us.
template = original.template;
}
/**
* <p>This method is used to allow objects in this base to notify us
* when instances of fields of this kind are changed. It is called
* from the {@link arlut.csd.ganymede.server.DBEditSet DBEditSet}
* {@link
* arlut.csd.ganymede.server.DBEditSet#commit_updateBases(java.util.Set)}
* method.</p>
*/
void updateTimeStamp()
{
lastChange = new Date();
}
/**
* <p>Returns a Date object containing the time that any changes were
* committed to instances of fields specified by this DBObjectBaseField.</p>
*/
public Date getTimeStamp()
{
return new Date(lastChange.getTime());
}
/**
* Returns true if any commits have been made to this DBObjectBase
* more recently than the comparison date.
*/
public boolean changedSince(Date comparison)
{
return lastChange.after(comparison);
}
/**
* Clears the editor reference from this DBObjectBaseField when
* schema editing is completed and updates the saved FieldTemplate.
*/
public synchronized void clearEditor()
{
this.editor = null;
this.inUseCache = null;
this.template = new FieldTemplate(this);
}
/**
* <p>This method is used when the database is being dumped, to write
* out this field definition to disk. It is mated with receive().</p>
*/
synchronized void emit(DataOutput out) throws IOException
{
out.writeUTF(field_name);
out.writeShort(field_code);
out.writeShort(field_type);
if (comment == null)
{
out.writeUTF("");
}
else
{
out.writeUTF(comment);
}
if (tabName == null)
{
out.writeUTF("");
}
else
{
out.writeUTF(tabName); // added at file version 2.12
}
out.writeBoolean(visibility); // added at file version 1.6
out.writeBoolean(array);
if (array)
{
out.writeShort(limit);
}
if (isBoolean())
{
out.writeBoolean(labeled);
if (labeled)
{
out.writeUTF(trueLabel);
out.writeUTF(falseLabel);
}
}
else if (isString())
{
out.writeShort(minLength);
out.writeShort(maxLength);
if (okChars == null)
{
out.writeUTF("");
}
else
{
out.writeUTF(okChars);
}
if (badChars == null)
{
out.writeUTF("");
}
else
{
out.writeUTF(badChars);
}
if (namespace != null)
{
out.writeUTF(namespace.getName());
}
else
{
out.writeUTF("");
}
out.writeBoolean(multiLine); // added at file version 1.9
if (regexpPat == null)
{
out.writeUTF(""); // added at file version 1.14
}
else
{
out.writeUTF(regexpPat); // added at file version 1.14
}
if (regexpDesc == null)
{
out.writeUTF(""); // added at file version 2.2
}
else
{
out.writeUTF(regexpDesc); // added at file version 2.2
}
}
else if (isNumeric())
{
if (namespace != null)
{
out.writeUTF(namespace.getName());
}
else
{
out.writeUTF("");
}
}
else if (isIP())
{
if (namespace != null)
{
out.writeUTF(namespace.getName());
}
else
{
out.writeUTF("");
}
}
else if (isInvid())
{
out.writeShort(allowedTarget);
out.writeBoolean(editInPlace);
out.writeShort(targetField);
}
else if (isPassword())
{
out.writeShort(minLength);
out.writeShort(maxLength);
if (okChars == null)
{
out.writeUTF("");
}
else
{
out.writeUTF(okChars);
}
if (badChars == null)
{
out.writeUTF("");
}
else
{
out.writeUTF(badChars);
}
// at 2.17 we introduce cracklib support for password fields
out.writeBoolean(cracklib_check);
// at 2.18 we introduce cracklib_supergash_exception,
// history_check, history_supergash_exception, and
// history_depth.
out.writeBoolean(cracklib_supergash_exception);
out.writeBoolean(history_check);
out.writeBoolean(history_supergash_exception);
out.writeInt(history_depth);
out.writeBoolean(crypted);
out.writeBoolean(md5crypted);
out.writeBoolean(apachemd5crypted);
out.writeBoolean(winHashed);
out.writeBoolean(sshaHashed);
// at 2.13 we introduce shaUnixCrypt support
out.writeBoolean(shaUnixCrypted);
out.writeBoolean(useShaUnixCrypted512);
out.writeInt(shaUnixCryptRounds);
// at 2.21 we introduce bCrypt support
out.writeBoolean(bCrypted);
out.writeInt(bCryptRounds);
out.writeBoolean(storePlaintext);
}
}
/**
* <p>This method is used when the database is being loaded, to read
* in this field definition from disk. It is mated with emit().</p>
*/
synchronized void receive(DataInput in) throws IOException
{
// we use setName to filter out any fieldname chars that wouldn't
// be acceptable as an XML entity name character.
field_name = in.readUTF();
field_code = in.readShort();
field_type = in.readShort();
if (base.getStore().isLessThan(2,16))
{
in.readUTF(); // vestigal classname
}
comment = in.readUTF();
// we stopped keeping the editable and removable flags in the
// ganymede.db file at 1.17
if (base.getStore().isLessThan(2,0))
{
in.readBoolean(); // skip editable
in.readBoolean(); // skip removable
}
// at file version 2.12, we introduce tab names per object base
// field
if (base.getStore().isAtLeast(2,12))
{
tabName = in.readUTF();
if (tabName.equals(""))
{
tabName = ts.l("receive.default_tab_name"); // "General"
}
}
else
{
tabName = ts.l("receive.default_tab_name"); // "General"
}
// at file version 1.6, we introduced field visibility
if (base.getStore().isAtLeast(1,6))
{
visibility = in.readBoolean();
}
else
{
visibility = true;
}
// at file version 1.7, we introduced an explicit built-in flag
// we took it out at 2.0
if (base.getStore().isBetweenRevs(1,7,2,0))
{
in.readBoolean(); // skip builtIn
}
// between file versions 1.1 and 1.17, we had a field_order
// field
if (base.getStore().isBetweenRevs(1,1,2,0))
{
tmp_displayOrder = in.readShort(); // skip field_order
}
else
{
tmp_displayOrder = -1;
}
array = in.readBoolean();
if (array)
{
limit = in.readShort();
}
else
{
limit = 1;
}
if (isBoolean())
{
labeled = in.readBoolean();
if (labeled)
{
trueLabel = in.readUTF();
falseLabel = in.readUTF();
}
}
else if (isString())
{
String nameSpaceId;
/* - */
minLength = in.readShort();
maxLength = in.readShort();
okChars = in.readUTF();
if (okChars.equals(""))
{
okChars = null;
}
badChars = in.readUTF();
if (badChars.equals(""))
{
badChars = null;
}
nameSpaceId = in.readUTF();
if (!nameSpaceId.equals(""))
{
setNameSpace(nameSpaceId);
}
// at file version 1.9, we introduced multiLine
if (base.getStore().isAtLeast(1,9))
{
multiLine = in.readBoolean();
}
else
{
multiLine = false;
}
// at file version 1.14, we introduced regexps for string fields
if (base.getStore().isAtLeast(1,14))
{
setRegexpPat(in.readUTF());
}
else
{
setRegexpPat(null);
}
// at file version 2.2, we introduced a description field for regexps
if (base.getStore().isAtLeast(2,2))
{
setRegexpDesc(in.readUTF());
}
else
{
setRegexpDesc(null);
}
}
else if (isNumeric())
{
String nameSpaceId;
/* - */
// at 1.8 we introduced namespaces for number fields
if (base.getStore().isAtLeast(1,8))
{
nameSpaceId = in.readUTF();
if (!nameSpaceId.equals(""))
{
setNameSpace(nameSpaceId);
}
}
}
else if (isIP())
{
String nameSpaceId;
/* - */
// at 1.8 we introduced namespaces for IP fields
if (base.getStore().isAtLeast(1,8))
{
nameSpaceId = in.readUTF();
if (!nameSpaceId.equals(""))
{
setNameSpace(nameSpaceId);
}
}
}
else if (isInvid())
{
allowedTarget = in.readShort();
editInPlace = in.readBoolean();
if (!editInPlace)
{
targetField = in.readShort();
}
else
{
// we no longer allow edit-in-place invid fields to target
// an explicit field in the embedded object. the
// relationship with the container field in the embedded
// object is now always implicit.
in.readShort();
}
// In DBStore file version 1.17 we dropped the use of the back
// links field. Some folks apparently used the schema editor
// to set an Invid Field to point to the backlinks field
// explicitly (rather than 'none'). We handle that here so
// that the code that takes care of handling asymmetric fields
// doesn't get confused.
if (targetField == SchemaConstants.BackLinksField)
{
targetField = -1;
}
}
else if (isPassword())
{
minLength = in.readShort();
maxLength = in.readShort();
okChars = in.readUTF();
if (okChars.equals(""))
{
okChars = null;
}
badChars = in.readUTF();
if (badChars.equals(""))
{
badChars = null;
}
// at 2.17 we introduce cracklib_check
if (base.getStore().isAtLeast(2,17))
{
cracklib_check = in.readBoolean();
}
else
{
cracklib_check = false;
}
// at 2.17, we introduce cracklib_supergash_exception,
// history_check, history_supergash_exception, and
// history_depth
if (base.getStore().isAtLeast(2,18))
{
cracklib_supergash_exception = in.readBoolean();
history_check = in.readBoolean();
history_supergash_exception = in.readBoolean();
history_depth = in.readInt();
}
crypted = in.readBoolean();
// at 1.16 we introduce md5crypted
if (base.getStore().isAtLeast(1,16))
{
md5crypted = in.readBoolean();
}
else
{
md5crypted = false;
}
// at 2.4 we introduce apachemd5crypted
if (base.getStore().isAtLeast(2,4))
{
apachemd5crypted = in.readBoolean();
}
else
{
apachemd5crypted = false;
}
// at 2.1 we introduced winHashed
if (base.getStore().isAtLeast(2,1))
{
winHashed = in.readBoolean();
}
else
{
winHashed = false;
}
// at 2.5 we introduced sshaHashed
if (base.getStore().isAtLeast(2,5))
{
sshaHashed = in.readBoolean();
}
else
{
sshaHashed = false;
}
// at 2.13 we introduce shaUnixCrypted
if (base.getStore().isAtLeast(2,13))
{
shaUnixCrypted = in.readBoolean();
useShaUnixCrypted512 = in.readBoolean();
shaUnixCryptRounds = in.readInt();
}
else
{
shaUnixCrypted = false;
useShaUnixCrypted512 = false;
shaUnixCryptRounds = 5000;
}
if (base.getStore().isAtLeast(2,21))
{
bCrypted = in.readBoolean();
bCryptRounds = in.readInt();
}
else
{
bCrypted = false;
bCryptRounds = 10;
}
// at 1.10 we introduced storePlaintext
if (base.getStore().isAtLeast(1,10))
{
storePlaintext = in.readBoolean();
}
else
{
storePlaintext = false;
}
}
}
/**
* <p>This method is used when the database is being dumped, to write
* out this field definition to disk. It is mated with setXML().</p>
*/
synchronized void emitXML(XMLDumpContext xmlOut) throws IOException
{
boolean nonEmpty = false;
/* -- */
xmlOut.startElementIndent("fielddef");
xmlOut.attribute("name", XMLUtils.XMLEncode(field_name));
xmlOut.attribute("id", java.lang.Short.toString(field_code));
xmlOut.indentOut();
if (comment != null && !comment.equals(""))
{
xmlOut.startElementIndent("comment");
xmlOut.write(comment);
xmlOut.endElement("comment");
}
if (!visibility)
{
xmlOut.startElementIndent("invisible");
xmlOut.endElement("invisible");
}
xmlOut.startElementIndent("typedef");
switch (field_type)
{
case FieldType.BOOLEAN:
xmlOut.attribute("type", "boolean");
break;
case FieldType.NUMERIC:
xmlOut.attribute("type", "numeric");
break;
case FieldType.FLOAT:
xmlOut.attribute("type", "float");
break;
case FieldType.FIELDOPTIONS:
xmlOut.attribute("type", "options");
break;
case FieldType.DATE:
xmlOut.attribute("type", "date");
break;
case FieldType.STRING:
xmlOut.attribute("type", "string");
break;
case FieldType.INVID:
xmlOut.attribute("type", "invid");
break;
case FieldType.PERMISSIONMATRIX:
xmlOut.attribute("type", "permmatrix");
break;
case FieldType.PASSWORD:
xmlOut.attribute("type", "password");
break;
case FieldType.IP:
xmlOut.attribute("type", "ip");
break;
default:
throw new RuntimeException("emitXML: unrecognized field type:" + field_type);
}
xmlOut.indentOut();
if (array)
{
nonEmpty = true;
xmlOut.startElementIndent("vector");
// A limit of 32767 does not need to be specified, as that is
// the highest vector size allowed, and so is implicit.
if (limit != Short.MAX_VALUE)
{
xmlOut.attribute("maxSize", java.lang.Short.toString(limit));
}
xmlOut.endElement("vector");
}
if (isBoolean())
{
if (labeled)
{
nonEmpty = true;
xmlOut.startElementIndent("labeled");
xmlOut.attribute("true", trueLabel);
xmlOut.attribute("false", falseLabel);
xmlOut.endElement("labeled");
}
}
else if (isString())
{
nonEmpty = true;
if (minLength != 0)
{
xmlOut.startElementIndent("minlength");
xmlOut.attribute("val", java.lang.Short.toString(minLength));
xmlOut.endElement("minlength");
}
// A limit of 32767 does not need to be specified, as that is
// the largest string size allowed, and so is implicit.
if (maxLength != Short.MAX_VALUE)
{
xmlOut.startElementIndent("maxlength");
xmlOut.attribute("val", java.lang.Short.toString(maxLength));
xmlOut.endElement("maxlength");
}
if (okChars != null && !okChars.equals(""))
{
xmlOut.startElementIndent("okchars");
xmlOut.attribute("val", okChars);
xmlOut.endElement("okchars");
}
if (badChars != null && !badChars.equals(""))
{
xmlOut.startElementIndent("badchars");
xmlOut.attribute("val", badChars);
xmlOut.endElement("badchars");
}
if (namespace != null)
{
xmlOut.startElementIndent("namespace");
xmlOut.attribute("val", namespace.getName());
xmlOut.endElement("namespace");
}
if (regexpPat != null && !regexpPat.equals(""))
{
xmlOut.startElementIndent("regexp");
xmlOut.attribute("val", regexpPat);
if (regexpDesc != null && !regexpDesc.equals(""))
{
xmlOut.attribute("desc", regexpDesc);
}
xmlOut.endElement("regexp");
}
if (multiLine)
{
xmlOut.startElementIndent("multiline");
xmlOut.endElement("multiline");
}
}
else if (isNumeric())
{
if (namespace != null)
{
nonEmpty = true;
xmlOut.startElementIndent("namespace");
xmlOut.attribute("val", namespace.getName());
xmlOut.endElement("namespace");
}
}
else if (isIP())
{
if (namespace != null)
{
nonEmpty = true;
xmlOut.startElementIndent("namespace");
xmlOut.attribute("val", namespace.getName());
xmlOut.endElement("namespace");
}
}
else if (isInvid())
{
if (allowedTarget != -1)
{
DBObjectBase targetObjectBase = null;
nonEmpty = true;
xmlOut.startElementIndent("targetobject");
if (allowedTarget == -2)
{
xmlOut.attribute("name", "*any*");
}
else
{
String targetObjectName = null;
targetObjectBase = base.getStore().getObjectBase(allowedTarget);
if (targetObjectBase != null)
{
targetObjectName = targetObjectBase.getName();
}
if (targetObjectName != null)
{
xmlOut.attribute("name", XMLUtils.XMLEncode(targetObjectName));
}
else
{
xmlOut.attribute("id", java.lang.Short.toString(allowedTarget));
}
}
xmlOut.endElement("targetobject");
if (targetField != -1 && targetField != SchemaConstants.BackLinksField)
{
boolean wroteLabel = false;
xmlOut.startElementIndent("targetfield");
if (targetObjectBase != null)
{
DBObjectBaseField targetFieldDef = targetObjectBase.getField(targetField);
if (targetFieldDef != null)
{
xmlOut.attribute("name", XMLUtils.XMLEncode(targetFieldDef.getName()));
wroteLabel = true;
}
}
if (!wroteLabel)
{
xmlOut.attribute("id", java.lang.Short.toString(targetField));
}
xmlOut.endElement("targetfield");
}
if (editInPlace)
{
xmlOut.startElementIndent("embedded");
xmlOut.endElement("embedded");
}
}
}
else if (isPassword())
{
nonEmpty = true;
if (minLength != 0)
{
xmlOut.startElementIndent("minlength");
xmlOut.attribute("val", java.lang.Short.toString(minLength));
xmlOut.endElement("minlength");
}
if (maxLength != Short.MAX_VALUE)
{
xmlOut.startElementIndent("maxlength");
xmlOut.attribute("val", java.lang.Short.toString(maxLength));
xmlOut.endElement("maxlength");
}
if (okChars != null && !okChars.equals(""))
{
xmlOut.startElementIndent("okchars");
xmlOut.attribute("val", okChars);
xmlOut.endElement("okchars");
}
if (badChars != null && !badChars.equals(""))
{
xmlOut.startElementIndent("badchars");
xmlOut.attribute("val", badChars);
xmlOut.endElement("badchars");
}
if (cracklib_check)
{
xmlOut.startElementIndent("cracklib_check");
if (cracklib_supergash_exception)
{
xmlOut.attribute("exception", "supergash");
}
xmlOut.endElement("cracklib_check");
}
if (history_check)
{
xmlOut.startElementIndent("history_check");
if (history_supergash_exception)
{
xmlOut.attribute("exception", "supergash");
}
if (history_depth > 0)
{
xmlOut.attribute("depth", Integer.toString(history_depth));
}
xmlOut.endElement("history_check");
}
if (crypted)
{
xmlOut.startElementIndent("crypted");
xmlOut.endElement("crypted");
}
if (md5crypted)
{
xmlOut.startElementIndent("md5crypted");
xmlOut.endElement("md5crypted");
}
if (apachemd5crypted)
{
xmlOut.startElementIndent("apacheMd5crypted");
xmlOut.endElement("apacheMd5crypted");
}
if (winHashed)
{
xmlOut.startElementIndent("winHashed");
xmlOut.endElement("winHashed");
}
if (sshaHashed)
{
xmlOut.startElementIndent("sshaHashed");
xmlOut.endElement("sshaHashed");
}
if (bCrypted)
{
xmlOut.startElementIndent("bCrypted");
xmlOut.attribute("rounds", java.lang.Integer.toString(bCryptRounds));
xmlOut.endElement("bCrypted");
}
if (shaUnixCrypted)
{
xmlOut.startElementIndent("shaUnixCrypted");
xmlOut.attribute("type", useShaUnixCrypted512 ? "512" : "256");
xmlOut.attribute("rounds", java.lang.Integer.toString(shaUnixCryptRounds));
xmlOut.endElement("shaUnixCrypted");
}
if (storePlaintext)
{
xmlOut.startElementIndent("plaintext");
xmlOut.endElement("plaintext");
}
}
xmlOut.indentIn();
if (nonEmpty)
{
xmlOut.indent();
}
xmlOut.endElement("typedef");
xmlOut.indentIn();
xmlOut.endElementIndent("fielddef");
}
/**
* <p>This method is used to read the definition for this
* DBObjectBaseField from a <fielddef> XMLItem tree.</p>
*/
synchronized ReturnVal setXML(XMLItem root, boolean doLinkResolve, PrintWriter err)
{
XMLItem item;
Integer field_codeInt;
boolean typeRead = false;
boolean _visibility = true;
String _comment = null;
ReturnVal retVal = null;
/* -- */
if (!isEditing())
{
// "Not in a schema editing context."
throw new IllegalStateException(ts.l("global.not_editing_schema"));
}
if (root == null || !root.matches("fielddef"))
{
// "DBObjectBaseField.setXML(): next element != open fielddef: {0}"
throw new IllegalArgumentException(ts.l("setXML.bad_nextitem", root));
}
field_codeInt = root.getAttrInt("id");
if (field_codeInt == null)
{
// "XML"
// "fielddef does not define id attr:\n{0}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.no_id", root.getTreeString()));
}
// extract the short
short _fieldID = field_codeInt.shortValue();
// we don't allow the xml file to specify global fields
if (_fieldID < 100)
{
// "XML"
// "fielddef defines an id attr out of range.. must be >= 100 for custom fields:\n{0}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.bad_id", root.getTreeString()));
}
// we have to set the id before we do anything else, since most
// setters refuse to set if the field id isn't in a safe range
retVal = setID(_fieldID);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not have its id set:\n{0}\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.id_failure", root.getTreeString(), retVal.getDialogText()));
}
// swap names if needed.. the DBObjectBase.setXML() will have checked for unique field
// names before calling us
retVal = setName(XMLUtils.XMLDecode(root.getAttrStr("name")), true);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not have its name set:\n{0}\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.name_failure", root.getTreeString(), retVal.getDialogText()));
}
// look at the nodes under root, set up this field def based on
// them
XMLItem children[] = root.getChildren();
for (int i = 0; i < children.length; i++)
{
item = children[i];
if (item.matches("comment"))
{
XMLItem commentChildren[] = item.getChildren();
if (commentChildren == null)
{
_comment = null;
continue;
}
if (commentChildren.length != 1)
{
// "XML"
// "unrecognized children in comment block:\n{0}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.bad_commentChild", root.getTreeString()));
}
_comment = commentChildren[0].getString();
}
else if (item.matches("invisible"))
{
_visibility = false; // need a setter?
}
else if (item.matches("typedef"))
{
if (typeRead)
{
// "XML"
// "redundant type definition for this field:\n{0}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.dup_type", root.getTreeString()));
}
if (item.getAttrStr("type") == null)
{
// "XML"
// "typedef tag does not contain type attribute:\n{0}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.missing_type", root.getTreeString()));
}
if (item.getAttrStr("type").equals("float"))
{
// float has no subchildren, all we need to
// do is attempt to do a setType
retVal = setType(FieldType.FLOAT);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("options"))
{
// has no subchildren, all we need to
// do is attempt to do a setType
retVal = setType(FieldType.FIELDOPTIONS);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("date"))
{
// date has no subchildren, all we need to
// do is attempt to do a setType
retVal = setType(FieldType.DATE);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("permmatrix"))
{
// permmatrix has no subchildren, all we need to
// do is attempt to do a setType
retVal = setType(FieldType.PERMISSIONMATRIX);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("string"))
{
retVal = doStringXML(item);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("invid"))
{
retVal = doInvidXML(item, doLinkResolve);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("numeric"))
{
retVal = doNumericXML(item);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("password"))
{
retVal = doPasswordXML(item);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("ip"))
{
retVal = doIPXML(item);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else if (item.getAttrStr("type").equals("boolean"))
{
retVal = doBooleanXML(item);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
else
{
// "XML"
// "typedef tag does not contain recognizable type attribute: {0} in fielddef tree:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.unrecognized_type", item, root.getTreeString()));
}
typeRead = true;
}
else
{
// "XML"
// "unrecognized XML item: {0} in fielddef tree:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.unrecognized_item", item, root.getTreeString()));
}
}
// set the options
retVal = setComment(_comment);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set comment:\n{0}\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("setXML.failed_field_comment", root.getTreeString(), retVal.getDialogText()));
}
visibility = _visibility;
return null;
}
/**
* <p>This method takes care of doing everything required to
* take an XMLItem tree <fielddef type="string"> and
* update this field's schema information to match.</p>
*
* @return A failure ReturnVal if the schema for this field
* could not be set to match.
*/
private ReturnVal doStringXML(XMLItem root)
{
boolean _vect = false;
short _maxSize = java.lang.Short.MAX_VALUE;
short _minlength = 0;
short _maxlength = java.lang.Short.MAX_VALUE;
String _okChars = null;
String _badChars = null;
String _regexp = null;
String _regexp_desc = null;
boolean _multiline = false;
String _namespace = null;
ReturnVal retVal;
/* -- */
if (!root.getAttrStr("type").equals("string"))
{
// "bad XMLItem tree:\n{0}"
throw new IllegalArgumentException(ts.l("global.badItemTree", root));
}
retVal = setType(FieldType.STRING);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
// the <typedef type="string"> node can have children
// of its own
XMLItem typeChildren[] = root.getChildren();
if (typeChildren != null)
{
for (int j = 0; j < typeChildren.length; j++)
{
XMLItem child = typeChildren[j];
if (child.matches("vector"))
{
_vect = true;
Integer vectSize = child.getAttrInt("maxSize");
if (vectSize != null)
{
_maxSize = vectSize.shortValue();
}
}
else if (child.matches("minlength"))
{
Integer val = child.getAttrInt("val");
if (val != null)
{
_minlength = val.shortValue();
}
}
else if (child.matches("maxlength"))
{
Integer val = child.getAttrInt("val");
if (val != null)
{
_maxlength = val.shortValue();
}
}
else if (child.matches("okchars"))
{
_okChars = child.getAttrStr("val");
}
else if (child.matches("badchars"))
{
_badChars = child.getAttrStr("val");
}
else if (child.matches("regexp"))
{
_regexp = child.getAttrStr("val");
_regexp_desc = child.getAttrStr("desc");
}
else if (child.matches("multiline"))
{
_multiline = true;
}
else if (child.matches("namespace"))
{
_namespace = child.getAttrStr("val");
}
else
{
// "XML"
// "Unrecognized string typedef entity: {0}\nIn field def:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_string_typedef_item",
child,
root.getTreeString()));
}
}
}
// now do all the setting
retVal = setArray(_vect);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set vector bit to {0}:\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_vector_op",
Boolean.valueOf(_vect),
root.getTreeString(),
retVal.getDialogText()));
}
if (_vect)
{
retVal = setMaxArraySize(_maxSize);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set vector maximum size: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_vector_limit",
Integer.valueOf(_maxSize),
root.getTreeString(),
retVal.getDialogText()));
}
}
retVal = setMinLength(_minlength);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set min length: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_min_length",
Integer.valueOf(_minlength),
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setMaxLength(_maxlength);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set max length: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_max_length",
Integer.valueOf(_maxlength),
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setOKChars(_okChars);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set ok chars: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_ok_chars",
_okChars,
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setBadChars(_badChars);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set bad chars: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_bad_chars",
_badChars,
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setRegexpPat(_regexp);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set regular expression: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_regexp",
_regexp,
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setRegexpDesc(_regexp_desc);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set regular expression description: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_regexp_desc",
_regexp_desc,
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setMultiLine(_multiline);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set multiline: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_multiline",
Boolean.valueOf(_multiline),
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setNameSpace(_namespace);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set namespace: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doStringXML.bad_namespace",
_namespace,
root.getTreeString(),
retVal.getDialogText()));
}
return null;
}
/**
* <p>This method takes care of doing everything required to
* take an XMLItem tree <fielddef type="boolean"> and
* update this field's schema information to match.</p>
*
* @return A failure ReturnVal if the schema for this field
* could not be set to match.
*/
private ReturnVal doBooleanXML(XMLItem root)
{
boolean _labeled = false;
String _trueLabel = null;
String _falseLabel = null;
ReturnVal retVal;
/* -- */
if (!root.getAttrStr("type").equals("boolean"))
{
// "bad XMLItem tree:\n{0}"
throw new IllegalArgumentException(ts.l("global.badItemTree", root));
}
retVal = setType(FieldType.BOOLEAN);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
// the <typedef type="boolean"> node can have children
// of its own
XMLItem typeChildren[] = root.getChildren();
if (typeChildren != null)
{
for (int j = 0; j < typeChildren.length; j++)
{
XMLItem child = typeChildren[j];
if (child.matches("labeled"))
{
_labeled = true;
_trueLabel = child.getAttrStr("true");
_falseLabel = child.getAttrStr("true");
}
else
{
// "XML"
// "Unrecognized boolean typedef entity: {0}\nIn field def:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doBooleanXML.bad_boolean_typedef_item",
child,
root.getTreeString()));
}
}
}
// now do all the setting
retVal = setLabeled(_labeled);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set labeled bit to {0}:\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doBooleanXML.bad_labeled_bit",
Boolean.valueOf(_labeled),
root.getTreeString(),
retVal.getDialogText()));
}
if (_labeled)
{
retVal = setTrueLabel(_trueLabel);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set true label to {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doBooleanXML.bad_true_label",
_trueLabel,
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setFalseLabel(_falseLabel);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set false label to {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doBooleanXML.bad_false_label",
_falseLabel,
root.getTreeString(),
retVal.getDialogText()));
}
}
return null;
}
/**
* <p>This method takes care of doing everything required to
* take an XMLItem tree <fielddef type="password"> and
* update this field's schema information to match.</p>
*
* @return A failure ReturnVal if the schema for this field
* could not be set to match.
*/
private ReturnVal doPasswordXML(XMLItem root)
{
short _minlength = 0;
short _maxlength = java.lang.Short.MAX_VALUE;
String _okChars = null;
String _badChars = null;
boolean _cracklib_check = false;
boolean _cracklib_supergash_exception = false;
boolean _history_check = false;
boolean _history_supergash_exception = false;
int _history_depth = 0;
boolean _crypted = false;
boolean _plaintext = false;
boolean _md5crypted = false;
boolean _apachemd5crypted = false;
boolean _winHashed = false;
boolean _sshaHashed = false;
boolean _bCrypted = false;
int _bCryptRounds = 10;
boolean _shaUnixCrypted = false;
boolean _shaUnixCrypt512 = false;
int _shaUnixCryptRounds = 5000;
ReturnVal retVal;
/* -- */
if (!root.getAttrStr("type").equals("password"))
{
// "bad XMLItem tree:\n{0}"
throw new IllegalArgumentException(ts.l("global.badItemTree", root));
}
retVal = setType(FieldType.PASSWORD);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
// the <typedef type="password"> node can have children
// of its own
XMLItem typeChildren[] = root.getChildren();
if (typeChildren != null)
{
for (int j = 0; j < typeChildren.length; j++)
{
XMLItem child = typeChildren[j];
if (child.matches("minlength"))
{
Integer val = child.getAttrInt("val");
if (val != null)
{
_minlength = val.shortValue();
}
}
else if (child.matches("maxlength"))
{
Integer val = child.getAttrInt("val");
if (val != null)
{
_maxlength = val.shortValue();
}
}
else if (child.matches("okchars"))
{
_okChars = child.getAttrStr("val");
}
else if (child.matches("badchars"))
{
_badChars = child.getAttrStr("val");
}
else if (child.matches("cracklib_check"))
{
_cracklib_check = true;
if ("supergash".equals(child.getAttrStr("exception")))
{
_cracklib_supergash_exception = true;
}
}
else if (child.matches("history_check"))
{
_history_check = true;
if ("supergash".equals(child.getAttrStr("exception")))
{
_history_supergash_exception = true;
}
Integer history_depth = child.getAttrInt("depth");
if (history_depth != null)
{
_history_depth = history_depth.intValue();
}
}
else if (child.matches("crypted"))
{
_crypted = true;
}
else if (child.matches("md5crypted"))
{
_md5crypted = true;
}
else if (child.matches("apacheMd5crypted"))
{
_apachemd5crypted = true;
}
else if (child.matches("winHashed"))
{
_winHashed = true;
}
else if (child.matches("sshaHashed"))
{
_sshaHashed = true;
}
else if (child.matches("bCrypted"))
{
_bCrypted = true;
Integer roundCount = child.getAttrInt("rounds");
if (roundCount != null)
{
_bCryptRounds = roundCount.intValue();
if (_bCryptRounds < 4 || _bCryptRounds > 31)
{
// "fielddef tried to set an invalid bcrypt round count (must be >= 4 and <= 31): {0}\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_bcrypt_rounds",
child, root.getTreeString()));
}
}
}
else if (child.matches("shaUnixCrypted"))
{
_shaUnixCrypted = true;
String typeVal = child.getAttrStr("type");
if (typeVal == null || typeVal.equals("256"))
{
_shaUnixCrypt512 = false;
}
else if (typeVal.equals("512"))
{
_shaUnixCrypt512 = true;
}
else
{
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_sha_unix_type",
child, root.getTreeString()));
}
Integer roundCount = child.getAttrInt("rounds");
if (roundCount != null)
{
_shaUnixCryptRounds = roundCount.intValue();
if (_shaUnixCryptRounds < 1000 || _shaUnixCryptRounds > 999999999)
{
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_sha_unix_rounds",
child, root.getTreeString()));
}
}
}
else if (child.matches("plaintext"))
{
_plaintext = true;
}
else
{
// "XML"
// "Unrecognized password typedef entity: {0}\nIn field def:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_password_typedef_item",
child, root.getTreeString()));
}
}
}
// now do all the setting
retVal = setMinLength(_minlength);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set min length: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_min_length",
Integer.valueOf(_minlength),
root.getTreeString(), retVal.getDialogText()));
}
retVal = setMaxLength(_maxlength);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set max length: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_max_length", Integer.valueOf(_maxlength),
root.getTreeString(), retVal.getDialogText()));
}
retVal = setOKChars(_okChars);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set ok chars: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_ok_chars", _okChars,
root.getTreeString(), retVal.getDialogText()));
}
retVal = setBadChars(_badChars);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set bad chars: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_bad_chars", _badChars,
root.getTreeString(), retVal.getDialogText()));
}
retVal = setCracklibChecked(_cracklib_check, _cracklib_supergash_exception);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set cracklib_check flags: {0}, {1}\n{2}\n{3}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_cracklib",
Boolean.valueOf(_cracklib_check), Boolean.valueOf(_cracklib_supergash_exception),
root.getTreeString(), retVal.getDialogText()));
}
retVal = setHistoryChecked(_history_check, _history_supergash_exception, _history_depth);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set history_check flags: {0}, {1}, {2}\n{3}\n{4}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_history_check",
Boolean.valueOf(_history_check), Boolean.valueOf(_history_supergash_exception), _history_depth,
root.getTreeString(), retVal.getDialogText()));
}
retVal = setCrypted(_crypted);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set crypted flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_crypted", Boolean.valueOf(_crypted),
root.getTreeString(), retVal.getDialogText()));
}
retVal = setMD5Crypted(_md5crypted);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set md5 crypted flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_md5_crypted", Boolean.valueOf(_md5crypted),
root.getTreeString(), retVal.getDialogText()));
}
retVal = setApacheMD5Crypted(_apachemd5crypted);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set md5 crypted flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_apache_md5_crypted",
Boolean.valueOf(_apachemd5crypted),
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setWinHashed(_winHashed);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set windows hashing flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_windows_hashed",
Boolean.valueOf(_winHashed),
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setSSHAHashed(_sshaHashed);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set SSHA hashing flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_ssha_hashed",
Boolean.valueOf(_sshaHashed),
root.getTreeString(),
retVal.getDialogText()));
}
retVal = setBCrypted(_bCrypted);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set BCrypt hashing flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_bcrypt_hashed",
Boolean.valueOf(_bCrypted),
root.getTreeString(),
retVal.getDialogText()));
}
if (_bCrypted)
{
retVal = setBCryptRounds(_bCryptRounds);
if (!ReturnVal.didSucceed(retVal))
{
// we should already have caught any XML error above,
// here, so I'm not going to bother wrapping the error
return retVal;
}
}
retVal = setShaUnixCrypted(_shaUnixCrypted);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set SHA Unix Crypt hashing flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_sha_unix_crypted",
Boolean.valueOf(_shaUnixCrypted),
root.getTreeString(),
retVal.getDialogText()));
}
if (_shaUnixCrypted)
{
retVal = setShaUnixCrypted512(_shaUnixCrypt512);
if (!ReturnVal.didSucceed(retVal))
{
// we should already have caught any XML error above,
// here, so I'm not going to bother wrapping the error
return retVal;
}
retVal = setShaUnixCryptRounds(_shaUnixCryptRounds);
if (!ReturnVal.didSucceed(retVal))
{
// we should already have caught any XML error above,
// here, so I'm not going to bother wrapping the error
return retVal;
}
}
retVal = setPlainText(_plaintext);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set plaintext flag: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doPasswordXML.bad_plaintext",
Boolean.valueOf(_plaintext),
root.getTreeString(),
retVal.getDialogText()));
}
return null;
}
/**
* <p>This method takes care of doing everything required to
* take an XMLItem tree <fielddef type="ip"> and
* update this field's schema information to match.</p>
*
* @return A failure ReturnVal if the schema for this field
* could not be set to match.
*/
private ReturnVal doIPXML(XMLItem root)
{
boolean _vect = false;
short _maxSize = java.lang.Short.MAX_VALUE;
String _namespace = null;
ReturnVal retVal;
/* -- */
if (!root.getAttrStr("type").equals("ip"))
{
// "bad XMLItem tree:\n{0}"
throw new IllegalArgumentException(ts.l("global.badItemTree", root));
}
retVal = setType(FieldType.IP);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
// the <typedef type="ip"> node can have children
// of its own
XMLItem typeChildren[] = root.getChildren();
if (typeChildren != null)
{
for (int j = 0; j < typeChildren.length; j++)
{
XMLItem child = typeChildren[j];
if (child.matches("vector"))
{
_vect = true;
Integer vectSize = child.getAttrInt("maxSize");
if (vectSize != null)
{
_maxSize = vectSize.shortValue();
}
}
else if (child.matches("namespace"))
{
_namespace = child.getAttrStr("val");
}
else
{
// "XML"
// "Unrecognized IP typedef entity: {0}\nIn field def:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doIPXML.bad_ip_typedef_item",
child, root.getTreeString()));
}
}
}
// now do all the setting
retVal = setArray(_vect);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set vector bit to {0}:\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doIPXML.bad_vector_op",
Boolean.valueOf(_vect),
root.getTreeString(),
retVal.getDialogText()));
}
if (_vect)
{
retVal = setMaxArraySize(_maxSize);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set vector maximum size: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doIPXML.bad_vector_limit",
Integer.valueOf(_maxSize),
root.getTreeString(),
retVal.getDialogText()));
}
}
retVal = setNameSpace(_namespace);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set namespace: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doIPXML.bad_namespace",
_namespace,
root.getTreeString(),
retVal.getDialogText()));
}
return null;
}
/**
* <p>This method takes care of doing everything required to
* take an XMLItem tree <fielddef type="numeric"> and
* update this field's schema information to match.</p>
*
* @return A failure ReturnVal if the schema for this field
* could not be set to match.
*/
private ReturnVal doNumericXML(XMLItem root)
{
String _namespace = null;
ReturnVal retVal;
/* -- */
if (!root.getAttrStr("type").equals("numeric"))
{
// "bad XMLItem tree:\n{0}"
throw new IllegalArgumentException(ts.l("global.badItemTree", root));
}
retVal = setType(FieldType.NUMERIC);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
// the <typedef type="numeric"> node can have children
// of its own
XMLItem typeChildren[] = root.getChildren();
if (typeChildren != null)
{
for (int j = 0; j < typeChildren.length; j++)
{
XMLItem child = typeChildren[j];
if (child.matches("namespace"))
{
_namespace = child.getAttrStr("val");
}
else
{
// "XML"
// "Unrecognized numeric typedef entity: {0}\nIn field def:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doNumericXML.bad_numeric_typedef_item",
child, root.getTreeString()));
}
}
}
// now do all the setting
retVal = setNameSpace(_namespace);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set namespace: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doNumericXML.bad_namespace", _namespace,
root.getTreeString(), retVal.getDialogText()));
}
return null;
}
/**
* <p>This method takes care of doing everything required to
* take an XMLItem tree <fielddef type="invid"> and
* update this field's schema information to match.</p>
*
* @return A failure ReturnVal if the schema for this field
* could not be set to match.
*/
private ReturnVal doInvidXML(XMLItem root, boolean doLinkResolve)
{
boolean _vect = false;
short _maxSize = java.lang.Short.MAX_VALUE;
boolean _embedded = false;
String _targetobjectStr = null;
Integer _targetobject = null;
String _targetfieldStr = null;
Integer _targetfield = null;
ReturnVal retVal;
/* -- */
if (!root.getAttrStr("type").equals("invid"))
{
// "bad XMLItem tree:\n{0}"
throw new IllegalArgumentException(ts.l("global.badItemTree", root));
}
retVal = setType(FieldType.INVID);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
// the <typedef type="invid"> node can have children
// of its own
XMLItem typeChildren[] = root.getChildren();
if (typeChildren != null)
{
for (int j = 0; j < typeChildren.length; j++)
{
XMLItem child = typeChildren[j];
if (child.matches("vector"))
{
_vect = true;
Integer vectSize = child.getAttrInt("maxSize");
if (vectSize != null)
{
_maxSize = vectSize.shortValue();
}
}
else if (child.matches("targetobject"))
{
_targetobjectStr = XMLUtils.XMLDecode(child.getAttrStr("name"));
_targetobject = child.getAttrInt("id");
if (_targetobjectStr == null && _targetobject == null)
{
// "XML"
// "targetobject item does not specify name or id: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_target_def",
child, root.getTreeString(), retVal.getDialogText()));
}
}
else if (child.matches("targetfield"))
{
_targetfieldStr = XMLUtils.XMLDecode(child.getAttrStr("name"));
_targetfield = child.getAttrInt("id");
if (_targetfieldStr == null && _targetfield == null)
{
// "XML"
// "targetfield item does not specify name or id: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_target_field_def",
child, root.getTreeString(), retVal.getDialogText()));
}
}
else if (child.matches("embedded"))
{
_embedded = true;
}
else
{
// "XML"
// "Unrecognized invid typedef entity: {0}\nIn field def:\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_invid_typedef_item",
child, root.getTreeString()));
}
}
}
// now do all the setting
retVal = setArray(_vect);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set vector bit to {0}:\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_vector_op",
Boolean.valueOf(_vect), root.getTreeString(), retVal.getDialogText()));
}
if (_vect)
{
retVal = setMaxArraySize(_maxSize);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set vector maximum size: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_vector_limit", Integer.valueOf(_maxSize),
root.getTreeString(), retVal.getDialogText()));
}
}
if (doLinkResolve)
{
// first we try to set the target object type, if any
if (_targetobjectStr != null)
{
if (_targetobjectStr.equals("*any*"))
{
retVal = setTargetBase((short)-2);
}
else
{
retVal = setTargetBase(_targetobjectStr);
}
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set invid target base: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_invid_target_base",
_targetobjectStr,
root.getTreeString(),
retVal.getDialogText()));
}
}
else if (_targetobject != null)
{
retVal = setTargetBase(_targetobject.shortValue());
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set invid target base: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_invid_target_base_num",
_targetobject,
root.getTreeString(),
retVal.getDialogText()));
}
}
else // both null
{
retVal = setTargetBase(null);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not clear invid target base:\n{0}\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_null_target_base",
root.getTreeString(),
retVal.getDialogText()));
}
}
// then we try to set the target field, if any. we don't
// track target fields for edit-in-place invid fields, though.
if (_targetfieldStr != null && !_embedded)
{
retVal = setTargetField(_targetfieldStr);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set invid target field: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_invid_target_field",
_targetfieldStr,
root.getTreeString(),
retVal.getDialogText()));
}
}
else if (_targetfield != null && !_embedded)
{
retVal = setTargetField(_targetfield.shortValue());
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set invid target field: {0,number,#}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_invid_target_field_num",
_targetfield,
root.getTreeString(),
retVal.getDialogText()));
}
}
else // both null
{
retVal = setTargetField(null);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not clear invid target field:\n{0}\n{1}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_null_target_field",
root.getTreeString(),
retVal.getDialogText()));
}
}
}
retVal = setEditInPlace(_embedded);
if (!ReturnVal.didSucceed(retVal))
{
// "XML"
// "fielddef could not set embedded status: {0}\n{1}\n{2}"
return Ganymede.createErrorDialog(ts.l("global.xmlErrorTitle"),
ts.l("doInvidXML.bad_embedded_status",
Boolean.valueOf(_embedded),
root.getTreeString(),
retVal.getDialogText()));
}
return null;
}
// ----------------------------------------------------------------------
/**
* <p>This method returns true if this field is one of the
* system fields present in all objects.</p>
*/
public synchronized boolean isBuiltIn()
{
return this.getID() < 100;
}
/**
* <p>This method returns true if this field definition can be removed
* by the schema editor.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isRemovable()
{
return !isSystemField();
}
/**
* <p>This method returns true if this field
* is intended to be visible to the client normally,
* false otherwise.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isVisible()
{
return visibility;
}
/**
* <p>Method to set the visibility or invisibility of this field.</p>
*
* <p>Used by the DBStore to mark certain scratch fields as being
* permanently hidden without having to set a custom DBEditObject
* subclass to declare the non-visibility of the field.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized void setVisibility(boolean visibility)
{
securityCheck();
this.visibility = visibility;
}
/**
* Server-side method used to set the status of this field's
* isInUseCache.
*/
public synchronized void setIsInUse(Boolean val)
{
inUseCache = val;
}
/**
* This method returns true if there is any concern that there
* are fields of this type in use in the database. The schema
* editing system uses this method to prevent incompatible
* modifications to fields that are in use in the database.
*
* This method will always return false when the DBObjectBase is
* newly created, is being initialized, or is being loaded.
*
* At other times, this method may seek through the entire
* collection of objects held in the containing DBObjectBase to see
* if any instances of this field exist.
*/
private synchronized boolean isInUse()
{
switch (base.getEditingMode())
{
case CREATING:
case INITIALIZING:
case LOADING:
return false;
}
if (inUseCache == null)
{
inUseCache = Boolean.valueOf(this.getBase().fieldInUse(this));
}
return inUseCache.booleanValue();
}
/**
* <p>Returns the Base we are a part of.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized DBObjectBase getBase()
{
return base;
}
/**
* <p>Returns a FieldTemplate serializable field definition object
* for this field.</p>
*/
public synchronized FieldTemplate getTemplate()
{
if (template == null)
{
template = new FieldTemplate(this);
}
return template;
}
/**
* <p>Returns the name of this field</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getName()
{
return field_name;
}
/**
* <p>Sets the name of this field</p>
*
* @param name The new name to put in this field
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setName(String name)
{
return setName(name, false);
}
/**
* <p>Sets the name of this field</p>
*
* @param name The new name to put in this field
* @param swapIfNeeded If true, attempting to set this field's name
* to a name that is already taken in the object will result in this
* field's taking the new name from the other field and giving that
* other field its own name. Only intended for use by setXML(), which
* has higher-level code to check for uniqueness of names in an XML
* schema definition.
*/
public synchronized ReturnVal setName(String name, boolean swapIfNeeded)
{
securityCheck();
// if we aren't loading, don't allow messing with the global fields
if (isEditingProtectedBuiltInField())
{
// "Schema Editing Error"
// "Can''t change the name of a system field."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setName.system_field"));
}
if (name == null || name.equals(""))
{
// "Schema Editing Error"
// "Can''t have a null or empty name."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setName.null_name"));
}
// no change, no problem
if (name.equals(field_name))
{
return null;
}
if (!XMLNameValidator.isValidGanymedeName(name))
{
// "Schema Editing Error"
// ""{0}" is not an acceptable Ganymede field name.\n\nAll Ganymede field names must be acceptable XML element names, save that spaces are allowed and underscores are not."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setName.invalid_ganymede_name", name));
}
DBObjectBaseField otherField = getBase().getField(name);
if (otherField != null)
{
if (!swapIfNeeded)
{
// "Schema Editing Error"
// "Can''t set a duplicate field name, "{0}" is already taken."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setName.duplicate_name", name));
}
else
{
// the xml schema code is setting this name, and it will have checked
// to make sure the name is unique.. we'll give the field that already
// has the name we want our name in trade, and then take the new name
// ourselves. the xml schema code will fix it up when it goes to
// set the name on the other.
String oldName = this.field_name;
this.field_name = name;
ReturnVal retVal = otherField.setName(oldName);
if (!ReturnVal.didSucceed(retVal))
{
return retVal;
}
}
}
field_name = name;
return null;
}
/**
* Returns the name of the tab that is to contain this field on the client.
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getTabName()
{
return tabName;
}
/**
* Sets the name of the tab that is to contain this field on the client.
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setTabName(String s)
{
securityCheck();
if (s == null || s.equals(""))
{
// "Schema Editing Error"
// "Can''t have a null or empty tab name."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTabName.null_name"));
}
this.tabName = s;
return null;
}
/**
* <p>Returns the comment defined in the schema for this field</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getComment()
{
return comment;
}
/**
* <p>Sets the comment defined in the schema for this field</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setComment(String s)
{
securityCheck();
if (s == null || s.equals(""))
{
comment = null;
}
else
{
comment = s;
}
return null;
}
/**
* <p>Returns the field type</p>
*
* <p>Where type is one of the following
* constants defined in the {@link arlut.csd.ganymede.common.FieldType FieldType}
* interface:</p>
*
* <pre>
* static short BOOLEAN = 0;
* static short NUMERIC = 1;
* static short DATE = 2;
* static short STRING = 3;
* static short INVID = 4;
* static short PERMISSIONMATRIX = 5;
* static short PASSWORD = 6;
* static short IP = 7;
* static short FLOAT = 8;
* static short FIELDOPTIONS = 9;
* </pre>
*
* @see arlut.csd.ganymede.server.DBStore
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized short getType()
{
return field_type;
}
/**
* <p>Sets the {@link arlut.csd.ganymede.common.FieldType field
* type} for this field. Changing the basic type of a field that is
* already being used in the server will cause very bad things to
* happen. The right way to change an existing field is to delete
* the field, commit the schema edit, edit the schema again, and
* recreate the field with the desired field type.</p>
*
* <p>If the new field type is not string, invid, or IP, the field
* will be made a scalar field.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setType(short type)
{
securityCheck();
if (type < FIRSTFIELD || type > LASTFIELD)
{
// "Type argument out of range"
throw new IllegalStateException(ts.l("setType.bad_type"));
}
// if no change, no problem.
if (type == field_type)
{
return null;
}
if (isEditingProtectedGanymedeDefinedField())
{
// "Can''t change the type of a system field: {0}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
if (isInUse())
{
// "Can''t change the type of a field which is in use in the database: {0}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setType.in_use", this.toString()));
}
if (isInvid())
{
// need to check to make sure no other invid field definitions are
// pointing to this field somehow, else changing type might break
// that other field definition
}
field_type = type;
// only strings, invids, and ip fields can be vectors
if (!(isString() || isInvid() || isIP()))
{
array = false;
}
return null;
}
// type identification convenience methods
/**
* <p>Returns true if this field is of boolean type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isBoolean()
{
return (field_type == BOOLEAN);
}
/**
* <p>Returns true if this field is of numeric type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isNumeric()
{
return (field_type == NUMERIC);
}
/**
* <p>Returns true if this field is of float type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isFloat()
{
return (field_type == FLOAT);
}
/**
* <p>Returns true if this field is of float type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isFieldOptions()
{
return (field_type == FIELDOPTIONS);
}
/**
* <p>Returns true if this field is of date type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isDate()
{
return (field_type == DATE);
}
/**
* <p>Returns true if this field is of string type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isString()
{
return (field_type == STRING);
}
/**
* <p>Returns true if this field is of invid type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isInvid()
{
return (field_type == INVID);
}
/**
* <p>Returns true if this field is of permission matrix type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isPermMatrix()
{
return (field_type == PERMISSIONMATRIX);
}
/**
* <p>Returns true if this field is of password type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isPassword()
{
return (field_type == PASSWORD);
}
/**
* <p>Returns true if this field is of IP type</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isIP()
{
return (field_type == IP);
}
/**
* <p>Returns true if this field is a vector field, false otherwise.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isArray()
{
return array;
}
/**
* <p>Set this field to be a vector or scalar. If b is true, this field will
* be a vector, if false, scalar.</p>
*
* <p>Only strings, invid's, and ip fields may be vectors. Attempting to
* setArray(true) for other field types will cause an IllegalArgumentException
* to be thrown.</p>
*
* <p>It may be possible to compatibly handle the conversion from
* scalar to vector, but a vector to scalar change is an incompatible
* change.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setArray(boolean b)
{
securityCheck();
if (array == b)
{
return null;
}
if (isEditingProtectedGanymedeDefinedField())
{
// array-ness is way too critical to be edited, even in mildly variable system
// fields like username in the user object
// "Can''t change the vector status of a system field: {0}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setArray.any_system_field", this.toString()));
}
if (b != array && isInUse())
{
// "Can''t change the vector status of a field which is in use in the database: {0}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setArray.in_use", this.toString()));
}
if (b && !(isString() || isInvid() || isIP()))
{
// "Can''t set this field type ({0}) to be a vector field: {1}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setArray.bad_type", this.getTypeDesc(), this.toString()));
}
array = b;
return null;
}
/**
* <p>Returns id code for this field. Each field in a
* {@link arlut.csd.ganymede.server.DBObject DBObject}
* has a unique code which identifies the field. This code represents
* the field in the on-disk data store, and is used by
* {@link arlut.csd.ganymede.server.DBEditObject DBEditObject}
* to choose what field to change in the setField method.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized short getID()
{
return field_code;
}
/**
* <p>This method is used to set this field's id in the containing
* DBObjectBase. This method will return a failure if an id is
* selected which is already in use in another field in this object
* definition.</p>
*/
public synchronized ReturnVal setID(short id)
{
securityCheck();
if (id < 0)
{
// "Field id number {0,number,#} is out of range: {1}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setID.out_of_range", Integer.valueOf(id),
this.toString()));
}
// no change, no problem
if (id == field_code)
{
return null;
}
if (field_code >= 0)
{
// "Can''t change field id number for a previously created field definition: {0}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setID.already_set", this.toString()));
}
if (base.getField(id) != null)
{
// "Can''t set field id number {0,number,#} on field {1}. That field id number is already in use by another field definition."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setID.in_use", Integer.valueOf(id),
this.toString()));
}
field_code = id;
return null;
}
/**
* <p>Returns the object definition that this field is defined under.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public DBObjectBase base()
{
return base;
}
// **
// array attribute methods
// **
/**
* <p>Returns the array size limitation for this field if it is an array field</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized short getMaxArraySize()
{
if (!array)
{
throw new IllegalStateException(ts.l("global.not_array", this.toString()));
}
return limit;
}
/**
* <p>Set the maximum number of values allowed in this vector field.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setMaxArraySize(short limit)
{
securityCheck();
if (!array)
{
throw new IllegalStateException(ts.l("global.not_array", this.toString()));
}
// no change, no problem
if (limit == this.limit)
{
return null;
}
if (isEditingProtectedGanymedeDefinedField())
{
// array sizes need not be screwed with in the system fields
// "Can''t change the vector limits of a system field: {0}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setMaxArraySize.any_system_field", this.toString()));
}
this.limit = limit;
if (isEditing() && isInUse())
{
return warning1;
}
else
{
return null;
}
}
// **
// boolean attribute methods
// **
/**
* <p>Returns true if this is a boolean field with labels</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isLabeled()
{
if (!isBoolean())
{
throw new IllegalStateException(ts.l("global.not_boolean", this.toString()));
}
return labeled;
}
/**
* <p>Turn labeled choices on/off for a boolean field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a boolean type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setLabeled(boolean b)
{
securityCheck();
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
if (!isBoolean())
{
throw new IllegalStateException(ts.l("global.not_boolean", this.toString()));
}
labeled = b;
return null;
}
/**
* <p>Returns the true Label if this is a labeled boolean field</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a labeled boolean type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getTrueLabel()
{
if (isLabeled())
{
return trueLabel;
}
throw new IllegalStateException(ts.l("global.not_labeled_boolean", this.toString()));
}
/**
* <p>Sets the label associated with the true choice for this
* boolean field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a labeled boolean type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setTrueLabel(String label)
{
securityCheck();
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
if (isLabeled())
{
trueLabel = label;
}
else
{
throw new IllegalStateException(ts.l("global.not_labeled_boolean", this.toString()));
}
return null;
}
/**
* <p>Returns the false Label if this is a labeled boolean field</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a labeled boolean type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getFalseLabel()
{
if (isLabeled())
{
return falseLabel;
}
throw new IllegalStateException(ts.l("global.not_labeled_boolean", this.toString()));
}
/**
* <p>Sets the label associated with the false choice for this
* boolean field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a labeled boolean type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setFalseLabel(String label)
{
securityCheck();
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
if (isLabeled())
{
falseLabel = label;
}
else
{
throw new IllegalStateException(ts.l("global.not_labeled_boolean", this.toString()));
}
return null;
}
// **
// string attribute methods
// **
/**
* <p>Returns the minimum acceptable string length if this is a string or
* password field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized short getMinLength()
{
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
return minLength;
}
/**
* <p>Sets the minimum acceptable length for this string or password field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setMinLength(short val)
{
securityCheck();
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
// no change, no problem
if (val == minLength)
{
return null;
}
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
minLength = val;
if (isEditing() && isInUse())
{
return warning1;
}
else
{
return null;
}
}
/**
* <p>Returns the maximum acceptable string length if this is a string
* or password field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized short getMaxLength()
{
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
return maxLength;
}
/**
* <p>Sets the maximum acceptable length for this string or
* password field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setMaxLength(short val)
{
securityCheck();
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
// no change, no problem
if (val == maxLength)
{
return null;
}
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
maxLength = val;
if (isEditing() && isInUse())
{
return warning1;
}
else
{
return null;
}
}
/**
* <p>Returns the set of acceptable characters if this is a string field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getOKChars()
{
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
return okChars;
}
/**
* <p>Sets the set of characters that are allowed in this string or
* password field. If s is null, all characters by default
* are acceptable.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setOKChars(String s)
{
securityCheck();
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
if (s != null && s.equals(""))
{
okChars = null;
}
else
{
okChars = s;
}
if (isEditing() && isInUse())
{
return warning1;
}
else
{
return null;
}
}
/**
* <p>Returns the set of unacceptable characters if this is a
* string or password field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getBadChars()
{
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
return badChars;
}
/**
* <p>Sets the set of characters that are specifically disallowed in
* this string or password field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string or password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setBadChars(String s)
{
securityCheck();
if (!isString() && !isPassword())
{
throw new IllegalStateException(ts.l("global.not_string_or_password", this.toString()));
}
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
if (s != null && s.equals(""))
{
badChars = null;
}
else
{
badChars = s;
}
if (isEditing() && isInUse())
{
return warning1;
}
else
{
return null;
}
}
/**
* <p>Returns true if this string field is intended to be a multi-line
* field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isMultiLine()
{
if (!isString())
{
throw new IllegalStateException(ts.l("global.not_string", this.toString()));
}
return multiLine;
}
/**
* <p>Sets whether or not this string field should be presented as a
* multiline field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setMultiLine(boolean b)
{
securityCheck();
if (!isString())
{
throw new IllegalStateException(ts.l("global.not_string", this.toString()));
}
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
multiLine = b;
return null;
}
/**
* Getter for internal code. We don't need a matching setter, since
* we set the regexp through a regexp string via setRegexpPat().
*/
public synchronized java.util.regex.Pattern getRegexp()
{
return this.regexp;
}
/**
* <p>Returns the regexp pattern string constraining this string
* field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getRegexpPat()
{
if (!isString())
{
throw new IllegalStateException(ts.l("global.not_string", this.toString()));
}
return regexpPat;
}
/**
* <p>Returns the text description of the regexp pattern string
* constraining this string field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getRegexpDesc()
{
if (!isString())
{
throw new IllegalStateException(ts.l("global.not_string", this.toString()));
}
return regexpDesc;
}
/**
* <p>Sets the regexp pattern string constraining this string field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setRegexpPat(String s)
{
securityCheck();
if (!isString())
{
throw new IllegalStateException(ts.l("global.not_string", this.toString()));
}
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
if (s == null || s.equals(""))
{
regexpPat = null;
regexp = null;
return null;
}
else
{
try
{
regexp = java.util.regex.Pattern.compile(s);
}
catch (java.util.regex.PatternSyntaxException ex)
{
// "Schema Editing Error"
// "Bad regular expression syntax: {0}\n{1}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setRegexpPat.bad_pattern", s, ex));
}
regexpPat = s;
if (isEditing() && isInUse())
{
return warning1;
}
else
{
return null;
}
}
}
/**
* <p>Sets the text descriptionf or the regexp pattern string
* constraining this string field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setRegexpDesc(String s)
{
securityCheck();
if (!isString())
{
throw new IllegalStateException(ts.l("global.not_string", this.toString()));
}
if (isEditingProtectedBuiltInField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
if (s == null || s.equals(""))
{
regexpDesc = null;
}
else
{
regexpDesc = s;
}
return null;
}
/**
* <p>Returns the DBNameSpace that this string, numeric, or IP
* field is associated with.</p>
*/
public synchronized DBNameSpace getNameSpace()
{
// several pieces of code have already been written to expect a null
// value for a field's namespace if none is defined, regardless of
// field type. No need for us to be overly fastidious here.
return namespace;
}
/**
* <p>Returns the label of this string, numeric, or IP field's namespace.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getNameSpaceLabel()
{
// several pieces of code have already been written to expect a null
// value for a field's namespace if none is defined, regardless of
// field type. No need for us to be overly fastidious here.
if (namespace != null)
{
return namespace.getName();
}
else
{
return null;
}
}
/**
* <p>Set a namespace constraint for this string, numeric, or
* IP field.</p>
*
* <p>Note that this is intended to be called from the Schema Editor,
* and won't take effect until the next time the system is stopped
* and reloaded.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a string, numeric, or IP type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setNameSpace(String nameSpaceId)
{
securityCheck();
if (!isString() && !isNumeric() && !isIP())
{
// "Can''t set a namespace constraint on this kind of field ({0}): {1}"
throw new IllegalStateException(ts.l("setNameSpace.bad_type",
this.getTypeDesc(),
this.toString()));
}
if (isEditingProtectedBuiltInField())
{
// "DBObjectBaseField: Schema Editing Error"
// "Can''t edit system field: {0}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field", this.toString()));
}
// no change, no problem
if ((nameSpaceId == null || nameSpaceId.equals("")) && namespace == null)
{
return null;
}
if (namespace != null && nameSpaceId != null && !nameSpaceId.equals(""))
{
DBNameSpace matchingSpace = base.getStore().getNameSpace(nameSpaceId);
if (matchingSpace == namespace)
{
return null;
}
}
// see about doing the setting
if (nameSpaceId == null || nameSpaceId.equals(""))
{
// wouldn't it be nice if java had decent support for declared data structures?
if ((base.getTypeID() == SchemaConstants.UserBase && getID() == SchemaConstants.UserUserName) ||
(base.getTypeID() == SchemaConstants.PersonaBase && getID() == SchemaConstants.PersonaLabelField) ||
(base.getTypeID() == SchemaConstants.OwnerBase && getID() == SchemaConstants.OwnerNameField) ||
(base.getTypeID() == SchemaConstants.EventBase && getID() == SchemaConstants.EventToken) ||
(base.getTypeID() == SchemaConstants.RoleBase && getID() == SchemaConstants.RoleName) ||
(base.getTypeID() == SchemaConstants.TaskBase && getID() == SchemaConstants.TaskName) ||
(base.getTypeID() == SchemaConstants.SyncChannelBase && getID() == SchemaConstants.SyncChannelName))
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setNameSpace.need_namespace", this.toString()));
}
if (isEditing())
{
if (!namespace.isSchemaEditInProgress())
{
namespace.schemaEditCheckout();
}
namespace.schemaEditUnregister(base.getTypeID(), getID());
}
namespace = null;
return null;
}
else
{
// this field is associated with a namespace.
DBNameSpace oldNamespace = namespace;
namespace = null;
for (DBNameSpace tmpNS: base.getStore().nameSpaces)
{
if (tmpNS.getName().equalsIgnoreCase(nameSpaceId))
{
namespace = tmpNS;
}
}
if (isEditing())
{
if (oldNamespace != null && oldNamespace != namespace)
{
if (!oldNamespace.isSchemaEditInProgress())
{
oldNamespace.schemaEditCheckout();
}
oldNamespace.schemaEditUnregister(base.getTypeID(), getID());
}
if (namespace != null && namespace != oldNamespace)
{
if (!namespace.isSchemaEditInProgress())
{
namespace.schemaEditCheckout();
}
// make sure that we can allocate all values already attached to this
// field
boolean success = true;
DBField lastFieldTried = null;
for (DBObject obj: base.getObjects())
{
lastFieldTried = obj.getField(getID());
if (lastFieldTried == null)
{
continue;
}
if (!this.isArray())
{
success = namespace.schemaEditRegister(lastFieldTried.key(), lastFieldTried);
}
else
{
for (int i = 0; success && i < lastFieldTried.size(); i++)
{
success = namespace.schemaEditRegister(lastFieldTried.key(i), lastFieldTried);
}
}
if (!success)
{
String fieldDesc = lastFieldTried.toString();
String content = lastFieldTried.getValueString();
namespace.schemaEditUnregister(base.getTypeID(), getID());
namespace = oldNamespace;
// "Can''t set namespace constraint {0} on field
// {1} without violating namespace uniqueness
// constraint on previously registered
// values.\nField {2} had a conflict.\Value(s) in
// conflict:{3}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setNameSpace.can_not_apply",
nameSpaceId,
this.toString(),
fieldDesc,
content));
}
}
}
}
// if we didn't find it, complain.
if (namespace == null)
{
// "Error, could not find a namespace called {0} to set on field {1}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setNameSpace.no_such_namespace", nameSpaceId, this.toString()));
}
}
return null;
}
// **
// invid attribute methods
// **
/**
* <p>Returns true if this is an invid field which is intended as an editInPlace
* reference for the client's rendering.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isEditInPlace()
{
return editInPlace;
}
/**
* <p>Sets whether or not this field is intended as an editInPlace
* reference for the client's rendering.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setEditInPlace(boolean b)
{
securityCheck();
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
// no change, no harm
if (b == editInPlace)
{
return null;
}
if (isEditingProtectedGanymedeDefinedField())
{
// "Schema Editing Error"
// "Can''t change the type of a system field: {0}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
if (isInUse())
{
// "Schema Editing Error"
// "Can''t change the editInPlace status type of an Invid field which is in use in the database: {0}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setEditInPlace.in_use", this.toString()));
}
editInPlace = b;
if (editInPlace)
{
// we don't target specific fields with embedded invid
// fields.. the relationship with the container field in
// edit-in-place objects is implicit with embedded invid
// fields.
targetField = -1;
}
return null;
}
/**
* <p>Returns true if this is a target restricted invid field</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isTargetRestricted()
{
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
return (allowedTarget != -1);
}
/**
* <p>Return the object type that this invid field is constrained to point to, if set</p>
*
* <p>-1 means there is no restriction on target type.</p>
*
* <p>-2 means there is no restriction on target type, but there is a specified symmetric field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized short getTargetBase()
{
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
return allowedTarget;
}
/**
* <p>Sets the allowed target object code of this invid field to <val>.
* If val is -1, this invid field can point to objects of any type.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setTargetBase(short val)
{
securityCheck();
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
// no change, no harm
if (val == allowedTarget)
{
return null;
}
if (isEditingProtectedGanymedeDefinedField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
// -1 and -2 are valid possible targets
if (val == -1 || val == -2)
{
allowedTarget = val;
return null;
}
// if we're field 0 (owner field) and we're being told to point to
// the owner group base, we'll go ahead.
if (field_code == 0 && val == 0)
{
allowedTarget = val;
return null;
}
if (isEditing())
{
if (editor != null)
{
DBObjectBase b = editor.getBase(val);
if (b == null)
{
// "Schema Editing Error"
// "Can''t set the target base to base number {0,number,#}. No such base is defined: {0}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetBase.bad_target_num", Integer.valueOf(val)));
}
}
}
allowedTarget = val;
if (isEditing() && isInUse())
{
return warning2;
}
else
{
return null;
}
}
/**
* <p>Sets the allowed target object code of this invid field to <baseName>.
* If val is null, this invid field can point to objects of any type.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setTargetBase(String baseName)
{
securityCheck();
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
if (baseName == null)
{
if (allowedTarget == -1)
{
return null; // no change, no harm
}
if (isEditingProtectedGanymedeDefinedField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
allowedTarget = -1;
if (isEditing() && isInUse())
{
return warning2;
}
else
{
return null;
}
}
if (isEditing())
{
// we know we're editing with an editor, since the schema
// loading / initialization logic never uses names to
// reference bases.
DBObjectBase b = editor.getBase(baseName);
if (b == null)
{
// "Schema Editing Error"
// "Can''t set the target base for invid field {1} to base {0}. No such base is defined."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetBase.bad_target", baseName, this.toString()));
}
if (b.getTypeID() == allowedTarget)
{
return null; // no change, no harm
}
if (isEditingProtectedGanymedeDefinedField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
allowedTarget = b.getTypeID();
if (isInUse())
{
return warning2;
}
else
{
return null;
}
}
else
{
DBObjectBase b = base.getStore().getObjectBase(baseName);
// we're loading here.. i don't expect the DBStore
// initializeSchema() method to actually use base names, but
// if it does for some reason and that base hasn't been
// created yet, we're well within our rights to throw a
// NullPointerException here. -- jon
allowedTarget = b.getTypeID();
return null;
}
}
/**
* <p>If this field is a target restricted invid field, this method will return
* true if this field has a symmetry relationship to the target</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isSymmetric()
{
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
return ((allowedTarget != -1) && (targetField != -1));
}
/**
* <p>If this field is a target restricted invid field, this method will return
* a short indicating the field in the target object that the symmetry relation
* applies to.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized short getTargetField()
{
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
return targetField;
}
/**
* <p>Sets the field of the target object of this invid field that should
* be managed in the symmetry relationship if isSymmetric(). If
* val == -1, the targetField will be set to a value representing
* no selection.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setTargetField(short val)
{
securityCheck();
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
if (isEditInPlace() && val != -1)
{
// "Can''t set target field on an embedded invid field {0}."
throw new IllegalStateException(ts.l("setTargetField.no_embedded_target_field", this.toString()));
}
if (val == targetField)
{
return null; // no change, no harm
}
if (isEditingProtectedGanymedeDefinedField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
if (val < 0)
{
targetField = val;
if (isEditing() && isInUse())
{
return warning2;
}
else
{
return null;
}
}
if (allowedTarget == -1)
{
// "Can''t set target field on a non-symmetric invid field {0} to {1,number,#}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetField.asymmetry_num", this.toString(), Integer.valueOf(val)));
}
if (isEditing())
{
if (editor != null)
{
DBObjectBase b = editor.getBase(allowedTarget);
// we're looking up the object that we have pre-selected.. we
// should always set a target object before trying to set a
// field
if (b == null)
{
// "Can''t find object type {0,number,#} in order to set target field for {2} to {1,number,#}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetField.bad_base_num", Integer.valueOf(allowedTarget), Integer.valueOf(val),
this.toString()));
}
DBObjectBaseField bF = b.getField(val);
if (bF == null)
{
// "Can''t find target field numbered {0,number,#} in order to set target field for {1}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetField.bad_target_field_num", Integer.valueOf(val),
this.toString()));
}
}
}
// if we're loading rather than editing, we'll go ahead and set it
// regardless of whether the target base and field have been
// created yet
targetField = val;
if (isEditing() && isInUse())
{
return warning2;
}
else
{
return null;
}
}
/**
* <p>Sets the field of the target object of this invid field that should
* be managed in the symmetry relationship if isSymmetric(). If <fieldName>
* is null, the targetField will be cleared.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not an invid type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setTargetField(String fieldName)
{
Base b;
BaseField bF;
/* -- */
securityCheck();
if (!isInvid())
{
throw new IllegalStateException(ts.l("global.not_invid", this.toString()));
}
if (fieldName == null || fieldName.equals(""))
{
if (targetField == -1)
{
return null; // no change, no harm
}
if (isEditingProtectedGanymedeDefinedField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
targetField = -1;
if (isEditing() && isInUse())
{
return warning2;
}
else
{
return null;
}
}
// look for fieldName in the base currently specified in
// allowedTarget
if (allowedTarget == -1)
{
// "Can''t set target field on a non-symmetric invid field {0} to {1}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetField.asymmetry", this.toString(), fieldName));
}
// The schema loading and initializing logic doesn't use field
// names, so we know editor should be defined
b = editor.getBase(allowedTarget);
try
{
if (b == null)
{
// "Can''t find object type {0,number,#} in order to set target field for {2} to {1}"
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetField.bad_base", Integer.valueOf(allowedTarget),
fieldName, this.toString()));
}
bF = b.getField(fieldName);
if (bF == null)
{
// "Can''t find target field named {0} in order to set target field for {1}."
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("setTargetField.bad_target_field", fieldName, this.toString()));
}
if (bF.getID() == targetField)
{
return null; // no change, no harm, no warning needed
}
// remember, system fields are initialized outside of the
// context of the editing system, there should never be a
// reason to call setTargetField() on a system field when
// editing
if (isEditingProtectedGanymedeDefinedField())
{
return Ganymede.createErrorDialog(ts.l("global.schema_editing_error"),
ts.l("global.system_field_change_attempt", this.toString()));
}
targetField = bF.getID();
}
catch (RemoteException ex)
{
throw new RuntimeException("caught remote: " + ex);
}
if (isEditing() && isInUse())
{
return warning2;
}
else
{
return null;
}
}
/**
* <p>Returns true if this field is a password field that has been
* configured to have values submitted be checked against
* cracklib.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isCracklibChecked()
{
return cracklib_check;
}
/**
* <p>Returns true if this field is a password field that is
* cracklib checked which will allow supergash to disregard warnings
* about cracklib.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean hasCracklibCheckException()
{
return cracklib_supergash_exception;
}
/**
* <p>This method is used to specify that this password field
* should check passwords entered with org.solinger.cracklib.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @param use_cracklib Control whether cracklib is applied on this
* field at all.
* @param supergash_exception If true, supergash-level admins will
* be given an info dialog about a cracklib check failure, but will
* be allowed to ignore the warning.
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setCracklibChecked(boolean use_cracklib, boolean supergash_exception)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
if (use_cracklib)
{
cracklib_check = true;
cracklib_supergash_exception = supergash_exception;
}
else
{
cracklib_check = false;
cracklib_supergash_exception = false;
}
return null;
}
/**
* <p>Returns true if this field is a password field that has been
* configured to have values submitted be checked previous password
* values linked to this DBObject.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isHistoryChecked()
{
return history_check;
}
/**
* <p>Returns true if this field is a password field that is history
* checked which will allow supergash to disregard warnings about
* history repeats.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean hasHistoryCheckException()
{
return history_supergash_exception;
}
/**
* <p>Returns the number of historical password hash values that
* will be kept for this field.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized int getHistoryDepth()
{
return history_depth;
}
/**
* <p>This method is used to specify that this password field should
* check passwords against previous values associated with this
* field.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @param use_history Control whether history checks are performed
* on this field at all.
* @param supergash_exception If true, supergash-level admins will
* be given an info dialog about a cracklib check failure, but will
* be allowed to ignore the warning.
* @param depth An integer greater than or equal to zero which
* controls how many previous password hashes should be retained for
* history checking.
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setHistoryChecked(boolean use_history, boolean supergash_exception, int depth)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
if (use_history)
{
history_check = true;
history_supergash_exception = supergash_exception;
history_depth = depth;
}
else
{
history_check = false;
history_supergash_exception = false;
history_depth = 0;
}
return null;
}
/**
* <p>This method returns true if this is a password field that
* stores passwords in UNIX crypt format, and can thus accept
* pre-crypted passwords.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isCrypted()
{
return crypted;
}
/**
* <p>This method is used to specify that this password field
* should store passwords in UNIX crypt format. If passwords
* are stored in UNIX crypt format, they will not be kept in
* plaintext on disk, regardless of the setting of setPlainText().</p>
*
* <p>setCrypted() is not mutually exclusive with setMD5Crypted().</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setCrypted(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
crypted = b;
return null;
}
/**
* <p>This method returns true if this is a password field that
* stores passwords in OpenBSD/FreeBSD/PAM md5crypt() format, and
* can thus accept pre-crypted passwords.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isMD5Crypted()
{
return md5crypted;
}
/**
* <p>This method is used to specify that this password field should
* store passwords in OpenBSD/FreeBSD/PAM md5crypt() format. If
* passwords are stored in md5crypt() format, they will not be kept
* in plaintext on disk, unless isPlainText() returns true.</p>
*
* <p>setMD5Crypted() is not mutually exclusive with any other
* encryption or plaintext options.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setMD5Crypted(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
md5crypted = b;
return null;
}
/**
* <p>This method returns true if this is a password field that
* stores passwords in Apache md5crypt() format, and
* can thus accept pre-crypted passwords.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isApacheMD5Crypted()
{
return apachemd5crypted;
}
/**
* <p>This method is used to specify that this password field should
* store passwords in Apache md5crypt() format. If
* passwords are stored in Apache md5crypt() format, they will not be kept
* in plaintext on disk, unless isPlainText() returns true.</p>
*
* <p>setApacheMD5Crypted() is not mutually exclusive with any other
* encryption or plaintext options.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setApacheMD5Crypted(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
apachemd5crypted = b;
return null;
}
/**
* <p>This method returns true if this is a password field that will
* store passwords in the two hashing formats used by Samba/Windows,
* the older 14-char LANMAN hash, and the newer md5/Unicode hash
* used by Windows NT. If passwords are stored in the windows
* hashing formats, they will not be kept in plaintext on disk,
* unless isPlainText() returns true.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isWinHashed()
{
return winHashed;
}
/**
* <p>This method is used to specify that this password field should
* store passwords in the Samba/Windows hashing formats.</p>
*
* <p>setWinHashed() is not mutually exclusive with any other
* encryption or plaintext options.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setWinHashed(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
winHashed = b;
return null;
}
/**
* <p>This method returns true if this is a password field that will
* store passwords in the Netscape SSHA (salted SHA) hash format,
* used in LDAP. If passwords are stored in the SSHA hashing format,
* they will not be kept in plaintext on disk, unless isPlainText()
* returns true.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isSSHAHashed()
{
return sshaHashed;
}
/**
* <p>This method is used to specify that this password field should
* store passwords in the Netscape SSHA (salted SHA) LDAP format.</p>
*
* <p>setSSHAHashed() is not mutually exclusive with any other
* encryption or plaintext options.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setSSHAHashed(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
sshaHashed = b;
return null;
}
/**
* <p>This method returns true if this is a password field that will
* store passwords in the OpenBSD BCrypt format.</p>
*
* <p>If passwords are stored in the BCrypt format, they will not be
* kept in plaintext on disk, unless isPlainText() returns true.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isBCrypted()
{
return bCrypted;
}
/**
* <p>This method is used to specify that this password field should
* store passwords in the OpenBSD BCrypt format.</p>
*
* <p>setBCrypted() is not mutually exclusive with any other
* encryption or plaintext options.</p>
*
* <p>This method will throw an IllegalArgumentException if this field
* definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setBCrypted(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
bCrypted = b;
return null;
}
/**
* <p>This method returns the complexity factor (in the exponential
* number of rounds) to be applied to password hash text generated
* in this password field definition by the OpenBSD BCrypt
* format.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized int getBCryptRounds()
{
return bCryptRounds;
}
/**
* <p>This method is used to specify the complexity factor (in the
* exponential number of rounds) to be applied to password hash text
* generated in this password field definition by the OpenBSD
* BCrypt format.</p>
*
* <p>This method will throw an IllegalArgumentException if this
* field definition is not a BCrypt using password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setBCryptRounds(int n)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
if (!bCrypted)
{
// "Not a BCrypt-using password field: {0}"
throw new IllegalStateException(ts.l("global.not_bcrypt", this.toString()));
}
if (n < 4)
{
n = 4;
}
else if (n > 31)
{
n = 31;
}
bCryptRounds = n;
return null;
}
/**
* <p>This method returns true if this is a password field that will
* store passwords in the SHA Unix Crypt format, specified by Ulrich
* Drepper at http://people.redhat.com/drepper/sha-crypt.html.</p>
*
* <p>If passwords are stored in the SHA Unix Crypt format, they will
* not be kept in plaintext on disk, unless isPlainText() returns
* true.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isShaUnixCrypted()
{
return shaUnixCrypted;
}
/**
* <p>This method is used to specify that this password field should
* store passwords in the SHA Unix Crypt format, specified by Ulrich
* Drepper at http://people.redhat.com/drepper/sha-crypt.html.</p>
*
* <p>setShaUnixCrypted() is not mutually exclusive with any other
* encryption or plaintext options.</p>
*
* <p>This method will throw an IllegalArgumentException if this field
* definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setShaUnixCrypted(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
shaUnixCrypted = b;
return null;
}
/**
* <p>This method returns true if this is a shaUnixCrypted password
* field that will store passwords using the SHA512 variant of the
* SHA Unix Crypt format, specified by Ulrich Drepper at
* http://people.redhat.com/drepper/sha-crypt.html.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isShaUnixCrypted512()
{
return useShaUnixCrypted512;
}
/**
* <p>This method is used to specify that this password field should
* store passwords in the SHA512 variant of the SHA Unix Crypt
* format, specified by Ulrich Drepper at
* http://people.redhat.com/drepper/sha-crypt.html.</p>
*
* <p>This method will throw an IllegalArgumentException if this field
* definition is not a ShaUnixCrypt using password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setShaUnixCrypted512(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
if (!shaUnixCrypted)
{
throw new IllegalStateException(ts.l("global.not_shacrypt", this.toString()));
}
useShaUnixCrypted512 = b;
return null;
}
/**
* This method returns the complexity factor (in number of rounds)
* to be applied to password hash text generated in this password
* field definition by the SHA Unix Crypt format, specified by
* Ulrich Drepper at
* http://people.redhat.com/drepper/sha-crypt.html.
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized int getShaUnixCryptRounds()
{
return shaUnixCryptRounds;
}
/**
* This method is used to specify the complexity factor (in number
* of rounds) to be applied to password hash text generated in this
* password field definition by the SHA Unix Crypt format, specified
* by Ulrich Drepper at
* http://people.redhat.com/drepper/sha-crypt.html.
*
* This method will throw an IllegalArgumentException if this field
* definition is not a ShaUnixCrypt using password type.
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setShaUnixCryptRounds(int n)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
if (!shaUnixCrypted)
{
throw new IllegalStateException(ts.l("global.not_shacrypt", this.toString()));
}
if (n < 1000)
{
n = 1000;
}
else if (n > 999999999)
{
n = 999999999;
}
shaUnixCryptRounds = n;
return null;
}
/**
* <p>This method returns true if this is a password field that
* will keep a copy of the password in plaintext in the Ganymede
* server's on-disk database.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized boolean isPlainText()
{
return storePlaintext;
}
/**
* <p>This method is used to specify that this password field
* should keep a copy of the password in plaintext on disk,
* even if other hash methods are in use which could be
* used for Ganymede login authentication. If no hash methods
* are enabled for this password field, plaintext will be stored
* on disk even if isPlainText() returns false for this field definition.</p>
*
* <p>This method will throw an IllegalArgumentException if
* this field definition is not a password type.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized ReturnVal setPlainText(boolean b)
{
securityCheck();
if (!isPassword())
{
throw new IllegalStateException(ts.l("global.not_password", this.toString()));
}
storePlaintext = b;
return null;
}
// general convenience methods
/**
* Indicates whether methods on this DBObjectBaseField are being
* called in the context of the server being loaded.
*
* If this method returns false, many of the data setter methods in
* this class will only be permitted if this DBObjectBaseField is
* connected with a non-null DBSchemaEdit object.
*
* In general, isLoading() and isEditing() should never be true at
* the same time.
*/
private synchronized boolean isLoading()
{
return base.getStore().isLoading();
}
/**
* Indicates whether methods on this DBObjectBaseField are being
* called in the context of the schema being edited.
*
* If this method returns false, many of the data setter methods in
* this class will only be permitted if the DBStore is in loading
* mode.
*
* In general, isEditing() and isLoading() should never be true at
* the same time.
*/
private synchronized boolean isEditing()
{
switch (base.getEditingMode())
{
case LOCKED:
case LOADING:
return false;
case INITIALIZING:
case CREATING:
case EDITING:
return true;
default:
throw new RuntimeException("Unrecognized editing mode");
}
}
/**
* Returns true if we are attempting to edit one of the universal
* built-in fields at an inappropriate time.
*
* If the DBObjectBase is in INITIALIZING, LOADING, or CREATING
* mode, we'll allow the built-in fields to be edited, otherwise not
* so much.
*/
private synchronized boolean isEditingProtectedBuiltInField()
{
switch (base.getEditingMode())
{
case LOCKED:
return true;
case INITIALIZING:
case LOADING:
case CREATING:
return false;
case EDITING:
return getID() <= SchemaConstants.FinalSystemField;
default:
throw new RuntimeException("Unrecognized editing mode");
}
}
/**
* Returns true if we are attempting to edit any of the pre-defined
* fields that the Ganymede server depends on for its operation at
* an inappropriate time.
*
* If the DBObjectBase is in INITIALIZING or LOADING mode, we'll
* allow the built-in fields to be edited, otherwise not so much.
*
* Note that this method is more restrictive than
* isEditingProtectedBuiltInField(), which only protects the
* universal built-in fields.
* isEditingProtectedGanymedeDefinedField() protects the universal
* built-in fields, as well as the Ganymede defined fields in
* specific pre-defined object types (User, Role, Admin Persona,
* Task, etc.)
*/
private synchronized boolean isEditingProtectedGanymedeDefinedField()
{
switch (base.getEditingMode())
{
case LOCKED:
return true;
case INITIALIZING:
case LOADING:
case CREATING:
return false;
case EDITING:
return isSystemField();
default:
throw new RuntimeException("Unrecognized editing mode");
}
}
/**
* Checks to see if we are in either a loading or editing context.
*
* If we are in neither, we throw an exception up, so that a
* modified client can't screw with our schema without appropriate
* authorization.
*
* This is necessary because we make RMI references to
* DBObjectBaseField objects available to all Ganymede RMI clients,
* most of which have not been granted permission to modify our
* schema.
*/
private synchronized void securityCheck()
{
if (!isLoading() && !isEditing())
{
// "Not in a schema editing context."
throw new IllegalStateException(ts.l("global.not_editing_schema"));
}
}
/**
* <p>This method checks to see if this DBObjectBaseField corresponds to
* a system field
*/
private synchronized boolean isSystemField()
{
if (getID() <= SchemaConstants.FinalSystemField)
{
return true;
}
// wouldn't it be nice if java had decent support for declared data structures?
// basically we want to identify all fields in the Ganymede
// built-in object types that we have to be especially paranoid
// about.
switch (base.getTypeID())
{
case SchemaConstants.OwnerBase:
switch (getID())
{
case SchemaConstants.OwnerNameField:
case SchemaConstants.OwnerMembersField:
case SchemaConstants.OwnerObjectsOwned:
case SchemaConstants.OwnerCcAdmins:
case SchemaConstants.OwnerExternalMail:
return true;
}
break;
case SchemaConstants.PersonaBase:
switch (getID())
{
case SchemaConstants.PersonaNameField:
case SchemaConstants.PersonaPasswordField:
case SchemaConstants.PersonaGroupsField:
case SchemaConstants.PersonaAssocUser:
case SchemaConstants.PersonaPrivs:
case SchemaConstants.PersonaAdminConsole:
case SchemaConstants.PersonaAdminPower:
case SchemaConstants.PersonaMailAddr:
case SchemaConstants.PersonaLabelField:
return true;
}
break;
case SchemaConstants.RoleBase:
switch (getID())
{
case SchemaConstants.RoleName:
case SchemaConstants.RoleMatrix:
case SchemaConstants.RolePersonae:
case SchemaConstants.RoleDefaultMatrix:
case SchemaConstants.RoleDelegatable:
return true;
}
break;
case SchemaConstants.UserBase:
switch (getID())
{
case SchemaConstants.UserUserName:
case SchemaConstants.UserPassword:
case SchemaConstants.UserAdminPersonae:
return true;
}
break;
case SchemaConstants.EventBase:
switch (getID())
{
case SchemaConstants.EventToken:
case SchemaConstants.EventName:
case SchemaConstants.EventDescription:
case SchemaConstants.EventMailBoolean:
case SchemaConstants.EventMailToSelf:
case SchemaConstants.EventMailOwners:
case SchemaConstants.EventExternalMail:
return true;
}
break;
case SchemaConstants.ObjectEventBase:
switch (getID())
{
case SchemaConstants.ObjectEventToken:
case SchemaConstants.ObjectEventName:
case SchemaConstants.ObjectEventDescription:
case SchemaConstants.ObjectEventMailToSelf:
case SchemaConstants.ObjectEventObjectName:
case SchemaConstants.ObjectEventMailOwners:
case SchemaConstants.ObjectEventObjectType:
case SchemaConstants.ObjectEventExternalMail:
return true;
}
break;
case SchemaConstants.TaskBase:
switch (getID())
{
case SchemaConstants.TaskName:
case SchemaConstants.TaskClass:
case SchemaConstants.TaskRunOnCommit:
case SchemaConstants.TaskRunPeriodically:
case SchemaConstants.TaskPeriodUnit:
case SchemaConstants.TaskPeriodCount:
case SchemaConstants.TaskPeriodAnchor:
return true;
}
break;
case SchemaConstants.SyncChannelBase:
switch (getID())
{
case SchemaConstants.SyncChannelName:
case SchemaConstants.SyncChannelDirectory:
case SchemaConstants.SyncChannelServicer:
case SchemaConstants.SyncChannelFields:
case SchemaConstants.SyncChannelPlaintextOK:
case SchemaConstants.SyncChannelTypeString:
case SchemaConstants.SyncChannelFullStateFile:
case SchemaConstants.SyncChannelTypeNum:
case SchemaConstants.SyncChannelClassName:
return true;
}
}
return false;
}
/**
* <p>This method is intended to produce a human readable
* representation of this field definition's simple type attribute.
* This method should not be used programatically to determine this
* field's type information.</p>
*
* <p>This method is only for human information, and the precise
* results returned are subject to change at any time.</p>
*
* @see arlut.csd.ganymede.rmi.BaseField
*/
public synchronized String getTypeDesc()
{
String result;
switch (field_type)
{
case BOOLEAN:
result = ts.l("getTypeDesc.boolean"); // "boolean"
break;
case NUMERIC:
result = ts.l("getTypeDesc.numeric"); // "numeric"
break;
case FLOAT:
result = ts.l("getTypeDesc.float"); // "float"
break;
case FIELDOPTIONS:
result = ts.l("getTypeDesc.field_option"); // "field options"
break;
case DATE:
result = ts.l("getTypeDesc.date"); // "date"
break;
case STRING:
result = ts.l("getTypeDesc.string"); // "string"
break;
case INVID:
result = ts.l("getTypeDesc.invid"); // "invid"
break;
case PERMISSIONMATRIX:
result = ts.l("getTypeDesc.permission_matrix"); // "permission matrix"
break;
case PASSWORD:
result = ts.l("getTypeDesc.password"); // "password"
break;
case IP:
result = ts.l("getTypeDesc.ip_field"); // "i.p. field"
break;
default:
// "<<bad type code: " + field_type + " >>"
result = ts.l("getTypeDesc.bad_code", Integer.valueOf(field_type));
}
if (array)
{
return result + "[]";
}
else
{
return result;
}
}
// misc object and interface methods
public synchronized String toString()
{
return base.getName() + ":" + field_name;
}
/**
* <p>Returns the type id for this field definition as
* a Short, suitable for use in a hash.</p>
*/
public synchronized Short getKey()
{
return Short.valueOf(field_code);
}
/**
* We support hashCode based on field id number, not on value.
*/
public synchronized int hashCode()
{
return field_code;
}
/**
* We support equals based on field id number, not on value.
*/
public synchronized boolean equals(Object o)
{
if (o instanceof DBObjectBaseField)
{
return field_code == ((DBObjectBaseField) o).field_code;
}
return false;
}
/**
* This method implements the Comparable interface.
*
* We are comparable in terms of the field id number for this field.
*
* The o parameter can be a Short, a short (using Java 5
* autoboxing), or another DBObjectBaseField.
*/
public synchronized int compareTo(Object o)
{
if (o instanceof Number)
{
return field_code - ((Number) o).shortValue();
}
else
{
DBObjectBaseField otherField = (DBObjectBaseField) o;
return field_code - otherField.field_code;
}
}
// static warning methods
private static ReturnVal genWarning1()
{
ReturnVal retVal = new ReturnVal(true);
// "Schema Editor"
//
// "The requested change in this field's allowed options has been made and will be put into effect if you commit your schema change.
//
// This schema change will only affect new values entered into this field in the database. Pre-existing fields of this kind in
// the database may or may not satisfy your new constraint."
//
retVal.setDialog(new JDialogBuff(ts.l("genWarning1.title"),
ts.l("genWarning1.text"),
ts.l("genWarning1.ok"), // "OK"
null,
"ok.gif"));
return retVal;
}
private static ReturnVal genWarning2()
{
ReturnVal retVal = new ReturnVal(true);
// "Schema Editor"
//
// "The requested change in this field's allowed options has been made and will be put into effect if you commit your schema change.
//
// Because this schema change is being made while there are fields of this type active in the database, there may be a chance
// that this change will affect database consistency."
//
retVal.setDialog(new JDialogBuff(ts.l("genWarning2.title"),
ts.l("genWarning2.text"),
ts.l("genWarning2.ok"),
null,
"ok.gif"));
return retVal;
}
}