Java tutorial
/* Copyright (C) 2015, University of Kansas Center for Research * * Specify Software Project, specify@ku.edu, Biodiversity Institute, * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package edu.ku.brc.af.ui.forms.validation; import static edu.ku.brc.ui.UIHelper.setControlSize; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.math.BigDecimal; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Vector; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import javax.swing.undo.UndoManager; import org.apache.commons.lang.StringUtils; import org.apache.commons.validator.routines.BigDecimalValidator; import org.apache.commons.validator.routines.CurrencyValidator; import org.apache.log4j.Logger; import edu.ku.brc.af.prefs.AppPrefsCache; import edu.ku.brc.af.ui.forms.ViewFactory; import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterField; import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterIFace; import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterMgr; import edu.ku.brc.ui.ColorWrapper; import edu.ku.brc.ui.DocumentAdaptor; import edu.ku.brc.ui.UIHelper; import edu.ku.brc.ui.UIRegistry; /** * A Single JTextField that provides for "formatted" input. The format "mask" is define in XML * via the UIFieldFormatterMgr class. This is idea for text fields that have a standard size and a specific format (i.e. Dates) * The mask enables the "fields" and separators to be specifically defined. * * Note: This has a single text Field and is usually only used for Dates. * * @code_status Beta * * @author rods * */ @SuppressWarnings("serial") public class ValFormattedTextFieldSingle extends JTextField implements ValFormattedTextFieldIFace, UIRegistry.UndoableTextIFace { private static final Logger log = Logger.getLogger(ValFormattedTextFieldSingle.class); protected DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols(Locale.getDefault()); protected NumberFormat numberFormatter = NumberFormat.getNumberInstance(Locale.getDefault()); protected NumberFormat numIntFormatter = NumberFormat.getIntegerInstance(Locale.getDefault()); protected static ColorWrapper valTextColor = null; protected static ColorWrapper viewBGColor = null; protected static ColorWrapper requiredFieldColor = null; protected UIValidatable.ErrorType valState = UIValidatable.ErrorType.Valid; protected boolean isRequired = false; protected boolean isChanged = false; protected boolean isNew = false; protected boolean isViewOnly = false; protected boolean needsUpdating = false; protected List<DocumentListener> documentListeners = null; protected boolean doSetText = false; protected boolean isPartialOK = false; protected boolean isAutoFmtOn = true; protected JFormattedDoc document; protected String defaultValue = null; protected UIFieldFormatterIFace formatter; protected List<UIFieldFormatterField> fields = null; protected boolean isFromUIFmtOverride = false; protected Integer suggestedNumCols = null; protected Object origValue = null; protected UndoManager undoManager = null; //--- Background Drawing of faint text protected String bgStr = null; protected Point pnt = null; protected Color textColor = new Color(200, 200, 200); protected Insets inner; protected BigDecimalValidator bdValidator = null; /** * Constructor. */ protected ValFormattedTextFieldSingle() { // do nothing } /** * Constructor * @param formatter the formatter * @param isViewOnly is it for view mode * @param isPartialOK can only a part of the format be typed in (used for search forms) */ public ValFormattedTextFieldSingle(final UIFieldFormatterIFace formatter, final boolean isViewOnly, final boolean isPartialOK, final boolean addFocusListeners) { super(); init(formatter, isViewOnly, isPartialOK, null, addFocusListeners); } /** * Constructor * @param formatter the formatter * @param isViewOnly is it for view mode * @param isPartialOK can only a part of the format be typed in (used for search forms) */ public ValFormattedTextFieldSingle(final UIFieldFormatterIFace formatter, final boolean isViewOnly, final boolean isPartialOK) { super(); init(formatter, isViewOnly, isPartialOK, null, false); } /** * Constructor * @param formatterName the formatters name * @param isViewOnly is it for view mode * @param isPartialOK can only a part of the format be typed in (used for search forms) * @param suggestedNumCols suggested number of columns that can be bigger than the format */ public ValFormattedTextFieldSingle(final String formatterName, final boolean isViewOnly, final boolean isPartialOK, final Integer suggestedNumCols) { super(); init(UIFieldFormatterMgr.getInstance().getFormatter(formatterName), isViewOnly, isPartialOK, suggestedNumCols, false); } /** * @param formatterArg the formatter (can't be null) * @param isViewOnlyArg is it for view mode * @param isPartialOK can only a part of the format be typed in (used for search forms) * @param suggestedNumCols suggested number of columns that can be bigger than the format */ protected void init(final UIFieldFormatterIFace formatterArg, final boolean isViewOnlyArg, final boolean isPartialOKArg, final Integer suggNumCols, final boolean addFocusListeners) { setControlSize(this); isPartialOK = isPartialOKArg; this.isViewOnly = isViewOnlyArg; this.suggestedNumCols = suggNumCols; initColors(); inner = getInsets(); setFormatterInternal(formatterArg); //log.debug(formatter.getName()); int numCols; if (suggestedNumCols != null) { numCols = Math.max(suggestedNumCols, formatter.getUILength()); } else { numCols = formatter.getUILength(); } setColumns(numCols); addMouseListener(new MouseAdapter() { /* (non-Javadoc) * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent) */ @Override public void mousePressed(MouseEvent e) { //System.err.println(e); super.mousePressed(e); } }); if (addFocusListeners) { addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { log.debug("[" + getText() + "]"); ((JTextField) e.getSource()).selectAll(); //System.err.println(e); repaint(); } @Override public void focusLost(FocusEvent e) { isNew = false; validateState(); repaint(); } }); } setBackground(isRequired ? requiredFieldColor.getColor() : viewBGColor.getColor()); if (!isViewOnlyArg) { if (!formatterArg.isUserInputNeeded() && !isPartialOK && isAutoFmtOn) { ViewFactory.changeTextFieldUIForDisplay(this, false); } else { super.setEnabled(true); } } } /** * @param dl */ public void addDocumentListener(final DocumentListener dl) { if (documentListeners == null) { documentListeners = new Vector<DocumentListener>(); } documentListeners.add(dl); } /** * @param dl */ public void removeDocumentListener(final DocumentListener dl) { if (documentListeners != null) { documentListeners.remove(dl); } } /** * @param isNew */ public void setNew(boolean isNew) { this.isNew = isNew; } /** * @param isPartialOK the isPartialOK to set */ public void setPartialOK(final boolean isPartialOK) { boolean isDifferent = this.isPartialOK != isPartialOK; this.isPartialOK = isPartialOK; if (isDifferent) { setRequired(isRequired); // will adjust the color } } /** * Sets the formatter. * @param dataObjFormatterName the formatter to use */ protected void setFormatterInternal(final UIFieldFormatterIFace formatterArg) { if (formatterArg != null && formatter != formatterArg) { formatter = formatterArg; fields = formatter.getFields(); int requiredLength = formatter.getLength(); bgStr = formatter.toPattern(); document = new JFormattedDoc(this, formatter, requiredLength); document.setIgnoreLenForValidation(isPartialOK); setDocument(document); document.addDocumentListener(new DocumentAdaptor() { @Override protected void changed(DocumentEvent e) { isChanged = true; if (formatter.isLengthOK(getText().length())) { setState(formatter.isValid(getText()) ? UIValidatable.ErrorType.Valid : UIValidatable.ErrorType.Error); repaint(); } if (documentListeners != null) { for (DocumentListener dl : documentListeners) { dl.changedUpdate(null); } } } }); } } /** * @return the formatter */ public UIFieldFormatterIFace getFormatter() { return formatter; } /** * Sets the formatter and reset the current value into the new format. * @param dataObjFormatterName the formatter to use */ public void setFormatter(final UIFieldFormatterIFace formatter) { Object currentValue = isChanged ? getValue() : origValue; setFormatterInternal(formatter); setValue(currentValue, ""); } /** * Initializes the control. */ public void initColors() { if (valTextColor == null || requiredFieldColor == null || viewBGColor == null) { valTextColor = AppPrefsCache.getColorWrapper("ui", "formatting", "valtextcolor"); requiredFieldColor = AppPrefsCache.getColorWrapper("ui", "formatting", "requiredfieldcolor"); viewBGColor = AppPrefsCache.getColorWrapper("ui", "formatting", "viewfieldcolor"); } } /* (non-Javadoc) * @see javax.swing.JComponent#setBackground(java.awt.Color) */ public void setBackground(final Color bg) { super.setBackground(bg); } /* (non-Javadoc) * @see edu.ku.brc.af.ui.forms.validation.UIValidatable#isNotEmpty() */ public boolean isNotEmpty() { return !getText().isEmpty(); } /* (non-Javadoc) * @see java.awt.Component#paint(java.awt.Graphics) */ @Override public void paint(Graphics g) { super.paint(g); String text = getText(); //System.err.println("isViewOnly "+isViewOnly+" isEnabled() "+isEnabled()+ " ["+text+"] "+(text.length() < bgStr.length())); if (!isViewOnly && needsUpdating && isEnabled() && text != null && text.length() < bgStr.length()) { FontMetrics fm = g.getFontMetrics(); int w = fm.stringWidth(text); pnt = new Point(inner.left + w, inner.top + fm.getAscent()); Rectangle r = g.getClipBounds(); Dimension s = getSize(); Insets i2 = getBorder().getBorderInsets(this); int x = i2.left - 1; int y = i2.top - 1; //int ww = s.width - i2.right + 1; int hh = s.height - i2.bottom + 1; String str = bgStr.substring(text.length(), bgStr.length()); int bgW = fm.stringWidth(str); g.setClip(x + w, y, Math.min(x + bgW, g.getClipBounds().width - x), hh); g.setColor(textColor); g.drawString(str, pnt.x, pnt.y); g.setClip(r.x, r.y, r.width, r.height); // reset clip } //System.out.println(hashCode() + " isNew: " +isNew+" valState: "+valState+" isEnabled: "+isEnabled()); // 3/2/09 - rods - removing !isNew from the condition //if (!isNew && valState == UIValidatable.ErrorType.Error && isEnabled()) if (valState == UIValidatable.ErrorType.Error && isEnabled()) { UIHelper.drawRoundedRect((Graphics2D) g, isNew ? new Color(249, 249, 0) : valTextColor.getColor(), getSize(), 1); } else if (valState == UIValidatable.ErrorType.Incomplete && isEnabled()) { UIHelper.drawRoundedRect((Graphics2D) g, new Color(249, 249, 0), getSize(), 1); } } /* (non-Javadoc) * @see java.awt.Component#setEnabled(boolean) */ @Override public void setEnabled(boolean enabled) { boolean isNeeded = formatter.isUserInputNeeded(); if (enabled && isNeeded) { super.setEnabled(isNeeded); } else { super.setEnabled(enabled); } setBackground(isRequired && isEnabled() ? requiredFieldColor.getColor() : viewBGColor.getColor()); // rods - 09/05/08 - (Bug 5858) need to hide the default value when disabled if (enabled) { if (origValue == null && defaultValue != null) { setText(defaultValue, false); } } else { if (origValue == null && defaultValue != null) { setText("", false); } } } /* (non-Javadoc) * @see javax.swing.text.JTextComponent#setText(java.lang.String) */ protected void setText(final String text, final boolean notify) { document.setIgnoreNotify(!notify); if (StringUtils.isEmpty(text)) { bgStr = formatter.toPattern(); } doSetText = true; super.setText(text); doSetText = false; document.setIgnoreNotify(notify); repaint(); } /* (non-Javadoc) * @see javax.swing.text.JTextComponent#setText(java.lang.String) */ @Override public void setText(final String text) { setText(text, false); } /** * Returns true if the no validation errors, false if there are * @return true if the no validation errors, false if there are */ public boolean isOK() { return valState == UIValidatable.ErrorType.Valid; } /** * @param isViewOnly the isViewOnly to set */ public void setViewOnly(boolean isViewOnly) { this.isViewOnly = isViewOnly; } //-------------------------------------------------- //-- AutoNumberableIFace Interface //-------------------------------------------------- /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.AutoNumberableIFace#updateAutoNumbers() */ public void updateAutoNumbers() { if (isAutoFmtOn && needsUpdating && formatter.getAutoNumber() != null && !isViewOnly) { String nextNum = formatter.getNextNumber(getText()); if (StringUtils.isNotEmpty(nextNum)) { try { setValue(nextNum, nextNum); bgStr = ""; needsUpdating = false; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(ValFormattedTextFieldSingle.class, ex); ex.printStackTrace(); } } } } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.AutoNumberableIFace#isFormatterAutoNumber() */ public boolean isFormatterAutoNumber() { return formatter != null && formatter.getAutoNumber() != null; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.AutoNumberableIFace#setAutoNumberEnabled(boolean) */ public void setAutoNumberEnabled(boolean turnOn) { if (isAutoFmtOn != turnOn && formatter.isIncrementer()) { if (turnOn) { ViewFactory.changeTextFieldUIForDisplay(this, false); } else { JTextField tf = new JTextField(); // Cheap and easy way to get original UI ViewFactory.changeTextFieldUIForEdit(this, tf.getBorder(), tf.getForeground(), tf.getBackground(), tf.isOpaque()); } } isAutoFmtOn = turnOn; } //-------------------------------------------------- //-- UIValidatable Interface //-------------------------------------------------- /* (non-Javadoc) * @see edu.kui.brc.ui.validation.UIValidatable#valState() */ public boolean isInError() { return valState != UIValidatable.ErrorType.Valid; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#getState() */ public ErrorType getState() { return valState; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#setState(edu.ku.brc.ui.forms.validation.UIValidatable.ErrorType) */ public void setState(ErrorType state) { this.valState = state; } /* (non-Javadoc) * @see edu.kui.brc.ui.validation.UIValidatable#isRequired() */ public boolean isRequired() { return isRequired && !isViewOnly; } /* (non-Javadoc) * @see edu.kui.brc.ui.validation.UIValidatable#setRequired(boolean) */ public void setRequired(boolean isRequired) { if (!isViewOnly) { setBackground(isRequired && isEnabled() ? requiredFieldColor.getColor() : viewBGColor.getColor()); this.isRequired = isRequired; } } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#isChanged() */ public boolean isChanged() { return isChanged; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#setChanged(boolean) */ public void setChanged(boolean isChanged) { this.isChanged = isChanged; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#setAsNew(boolean) */ public void setAsNew(boolean isNew) { // Now that we now we know it is a new Object then set the default value if (isNew && origValue == null) { setText(StringUtils.isNotEmpty(defaultValue) ? defaultValue : ""); } this.isNew = isRequired || !formatter.isUserInputNeeded() ? isNew : false; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#reset() */ public void reset() { origValue = null; // rods - 04/06/09 - add 'true' so the notifications get sent // the textfield was getting into a very off state otherwise. // I think what really needs to be done: Is the document listeners // should be removed and then added. setText(StringUtils.isNotEmpty(defaultValue) ? defaultValue : "", true); validateState(); repaint(); } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#getValidatableUIComp() */ public Component getValidatableUIComp() { return this; } /* (non-Javadoc) * @see java.awt.Component#validate() */ public UIValidatable.ErrorType validateState() { if (isViewOnly) { valState = UIValidatable.ErrorType.Valid; } else if (formatter != null && formatter.isUserInputNeeded()) { String data = getText(); if (StringUtils.isEmpty(data)) { valState = isRequired ? UIValidatable.ErrorType.Incomplete : UIValidatable.ErrorType.Valid; } else if (formatter.isNumeric()) { valState = validateNumeric(data); } else { if (!document.isIgnoreLenForValidation()) { valState = formatter.isLengthOK(data.length()) ? UIValidatable.ErrorType.Valid : UIValidatable.ErrorType.Error; // Only validate against the formatter if the it is the right length if (valState == UIValidatable.ErrorType.Valid) { valState = formatter.isValid(data) ? UIValidatable.ErrorType.Valid : UIValidatable.ErrorType.Error; } } else { valState = UIValidatable.ErrorType.Valid; } } } else { valState = UIValidatable.ErrorType.Valid; } //System.out.println("#### validateState "+ getText()+" "+data.length() +" "+ requiredLength+" "+valState); return valState; } /* (non-Javadoc) * @see javax.swing.JTextField#getColumns() */ @Override public int getColumns() { int w = super.getColumns(); return w; } /* (non-Javadoc) * @see javax.swing.JTextField#getColumnWidth() */ @Override protected int getColumnWidth() { int w = super.getColumnWidth(); return w; } /* (non-Javadoc) * @see javax.swing.JTextField#setColumns(int) */ @Override public void setColumns(int arg0) { super.setColumns(arg0); } /** * Checks the number against the min and max in the formatter (they are not required). * @param val the value in question * @return true if within the min and max */ protected boolean isMinMaxOK(final Number val) { if (val != null) { Number maxValue = formatter.getMaxValue(); Number minValue = formatter.getMinValue(); //System.err.println("isMinMaxOK - min["+minValue+"] v["+val+"] max["+maxValue+"]"); if (minValue != null && maxValue != null) { //boolean ok = !(val.doubleValue() > maxValue.doubleValue() || val.doubleValue() < minValue.doubleValue()); //System.err.println("isMinMaxOK - ok["+ok+"]"); return !(val.doubleValue() > maxValue.doubleValue() || val.doubleValue() < minValue.doubleValue()); } } return true; } /** * @param value * @return */ protected UIValidatable.ErrorType validateNumeric(final String value) { Class<?> cls = formatter.getDataClass(); try { if (cls == BigDecimal.class) { if (bdValidator == null) { bdValidator = CurrencyValidator.getInstance(); } Number maxVal = formatter.getMaxValue(); Number minVal = formatter.getMinValue(); BigDecimal fooAmount = bdValidator.validate(value, Locale.getDefault()); // XXX FINAL RELEASE if (fooAmount == null) { // error...not a valid currency amount return UIValidatable.ErrorType.Error; } if (!bdValidator.minValue(fooAmount, minVal) || !bdValidator.maxValue(fooAmount, maxVal)) { // valid...in the specified range return UIValidatable.ErrorType.Error; } return UIValidatable.ErrorType.Valid; } else { try { if (cls == Long.class) { Number num = numIntFormatter.parse(value); Long val = Long.valueOf(num.toString()); return !isMinMaxOK(val) ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; } else if (cls == Integer.class) { Number num = numIntFormatter.parse(value); Integer val = Integer.valueOf(num.toString()); return !isMinMaxOK(val) ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; } else if (cls == Short.class) { Number num = numIntFormatter.parse(value); Short val = Short.valueOf(num.toString()); return !isMinMaxOK(val) ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; } else if (cls == Byte.class) { Number num = numIntFormatter.parse(value); Byte val = Byte.valueOf(num.toString()); return !isMinMaxOK(val) ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; } else if (cls == Double.class) { Number num = numberFormatter.parse(value); Double val = Double.valueOf(num.toString()); return !isMinMaxOK(val) ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; } else if (cls == Float.class) { Number num = numberFormatter.parse(value); Float val = Float.valueOf(num.toString()); return !isMinMaxOK(val) ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; } else { throw new RuntimeException("Missing case for numeric class [" + cls.getName() + "]"); } } catch (NumberFormatException fex) { return UIValidatable.ErrorType.Error; } } } catch (Exception ex) { } return UIValidatable.ErrorType.Error; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#cleanUp() */ public void cleanUp() { document = null; formatter = null; fields = null; } /* (non-Javadoc) * @see edu.ku.brc.ui.forms.validation.UIValidatable#getReason() */ public String getReason() { return null; } //-------------------------------------------------------- // UIRegistry.UndoableTextIFace //-------------------------------------------------------- /* (non-Javadoc) * @see edu.ku.brc.ui.UIRegistry.UndoableTextIFace#getUndoManager() */ public UndoManager getUndoManager() { if (undoManager == null) { undoManager = new UndoManager(); UIRegistry.getInstance().hookUpUndoableEditListener(this); } return undoManager; } /* (non-Javadoc) * @see edu.ku.brc.ui.UIRegistry.UndoableTextIFace#getTextComponent() */ public JTextComponent getTextComponent() { return this; } //-------------------------------------------------------- // GetSetValueIFace //-------------------------------------------------------- /* (non-Javadoc) * @see edu.ku.brc.af.ui.GetSetValueIFace#setValue(java.lang.Object, java.lang.String) */ public void setValue(Object value, String defaultValue) { this.defaultValue = defaultValue; if (document != null) document.setAllowText(true); String data; if (value != null) { if (value instanceof String) { data = (String) value; } else if (value instanceof Date) { data = formatter.getDateWrapper().format((Date) value); } else if (value instanceof Calendar) { data = formatter.getDateWrapper().format(((Calendar) value).getTime()); } else if (value instanceof Number) { if (formatter != null) { data = formatter.formatToUI(value).toString(); } else { data = value.toString(); } } else { data = value.toString(); } } else { // rods - 3/15/08 This was commented out because of AutoNumbering // and it was just set to " (empty). But for Dates we need to use the default // so I tested checking for "isDate" and it worked now let's try when // it isn't an AutoNumber. // // rods - 09/05/08 - (Bug 5858) need to hide the default value when disabled // so I added 'isEnabled()' below // data = isEnabled() && StringUtils.isNotEmpty(defaultValue) && formatter.getAutoNumber() == null && isNew ? defaultValue : ""; needsUpdating = true; } if (origValue == null) { origValue = value; } if (formatter.isInBoundFormatter()) { setText((String) formatter.formatToUI(data)); needsUpdating = StringUtils.isEmpty(data) && formatter.getAutoNumber() != null && formatter.isIncrementer(); } else { setText(data); } if (undoManager != null) { undoManager.discardAllEdits(); } if (document != null) document.setAllowText(false); validateState(); repaint(); } /** * @param isFromUIFmtOverride the isFromUIFmtOverride to set */ public void setFromUIFmtOverride(boolean isFromUIFmtOverride) { this.isFromUIFmtOverride = isFromUIFmtOverride; } /* (non-Javadoc) * @see edu.ku.brc.af.ui.GetSetValueIFace#getValue() */ public Object getValue() { if (formatter.isDate()) { String value = getText(); if (StringUtils.isNotEmpty(value)) { try { Calendar cal = Calendar.getInstance(); cal.setTime(formatter.getDateWrapper().getSimpleDateFormat().parse(value)); return cal; } catch (ParseException ex) { log.error("Date is in error for parsing[" + value + "]"); } } return null; } // else String val = getText(); if (formatter.isFromUIFormatter() && !isFromUIFmtOverride) { if (StringUtils.isNotEmpty(val)) { return formatter.formatFromUI(getText()); } } return val != null && val.isEmpty() ? null : val; } //------------------------------------------------- // JFormattedDoc //------------------------------------------------- public class JFormattedDoc extends ValPlainTextDocument { protected int docLimit; protected ValFormattedTextFieldSingle textField; protected UIFieldFormatterIFace docFormatter; protected UIFieldFormatterField[] docFields; protected boolean ignoreLenForValidation = false; protected boolean isReplacingSameSize = false; protected boolean allowText = false; /** * Create a special formatted document * @param textField the textfield the document is associated with * @param formatter the formatter * @param limit the length of the format */ public JFormattedDoc(final ValFormattedTextFieldSingle textField, final UIFieldFormatterIFace formatter, final int limit) { super(); this.textField = textField; this.docFormatter = formatter; this.docLimit = limit; this.docFields = new UIFieldFormatterField[limit]; int inx = 0; for (UIFieldFormatterField f : docFormatter.getFields()) { for (int i = 0; i < f.getSize(); i++) { docFields[inx++] = f; } } } /** * @return the ignoreLenForValidation */ public boolean isIgnoreLenForValidation() { return ignoreLenForValidation; } /** * @param ignoreLenForValidation the ignoreLenForValidation to set */ public void setIgnoreLenForValidation(boolean ignoreLenForValidation) { this.ignoreLenForValidation = ignoreLenForValidation; } /** * @param string * @return */ public boolean isNumber(final String string) { try { return (numberFormatter.parse(string) != null); } catch (ParseException e) { } return false; } /** * Check to see if the input was correct (doesn't check against the separator) * @param field the field info * @param str the str to be checked * @return true char matches the type of input, false it is in error */ protected boolean isCharOK(final UIFieldFormatterField field, final String str) { if (field.getType() == UIFieldFormatterField.FieldType.alpha && !StringUtils.isAlpha(str)) { return false; } else if (field.getType() == UIFieldFormatterField.FieldType.alphanumeric && !StringUtils.isAlphanumeric(str)) { return false; } else if (field.getType() == UIFieldFormatterField.FieldType.anychar) { return true; } else if (field.getType() == UIFieldFormatterField.FieldType.numeric) { char decSep = formatSymbols.getDecimalSeparator(); String str1 = StringUtils.remove(str, decSep); String str2 = StringUtils.remove(str1, '-'); if (StringUtils.isNumeric(str2)) { Class<?> cls = formatter.getDataClass(); if (cls == java.lang.Integer.class || cls == java.lang.Long.class || cls == java.lang.Short.class || cls == java.lang.Byte.class) { return str1.length() == str.length(); } return str2.length() == 0 || StringUtils.isNumericSpace(str2); } return false; } return true; } /** * @param allowText the allowText to set */ public void setAllowText(boolean allowText) { this.allowText = allowText; } /** * Checks to see if the incoming string maps correctly to the format and ll the chars match the appropriate type * @param str the string * @return true - ok, false there was an error */ protected boolean okToInsertText(final String str) { int len = Math.min(str.length(), docLimit); for (int i = 0; i < len; i++) { char c = str.charAt(i); if (docFields[i].getType() == UIFieldFormatterField.FieldType.separator) { if (c != docFields[i].getValue().charAt(0)) { return false; } } String s = ""; s += c; if (!isCharOK(docFields[i], s)) { return false; } } return true; } /* (non-Javadoc) * @see javax.swing.text.AbstractDocument#replace(int, int, java.lang.String, javax.swing.text.AttributeSet) */ @Override public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { if (docFormatter.isIncrementer() && !allowText && !isPartialOK && isAutoFmtOn) { return; } isReplacingSameSize = text.length() == length; super.replace(offset, length, text, attrs); } /* (non-Javadoc) * @see javax.swing.text.Document#remove(int, int) */ @Override public void remove(int offset, int len) { if (docFormatter.isIncrementer() && !allowText && !isPartialOK && isAutoFmtOn) { return; } /*UIFieldFormatterField field = docFields[offset]; // We can't let them try to delete separator's or incrementers if (!doSetText && len < limit && ((field.isIncrementer() && isAutoFmtOn) || field.isByYear() || docFields[offset].getType() == UIFieldFormatterField.FieldType.separator)) { int pos = getCaretPosition(); setCaretPosition(pos - field.getSize()); return; }*/ try { int l = formatter.isNumeric() || formatter.isDate() || isReplacingSameSize ? len : this.getLength() - offset; super.remove(offset, l); validateState(); } catch (BadLocationException ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(ValFormattedTextFieldSingle.class, ex); throw new RuntimeException(ex); } } /* (non-Javadoc) * @see javax.swing.text.Document#insertString(int, java.lang.String, javax.swing.text.AttributeSet) */ @Override public void insertString(final int offset, final String strArg, final AttributeSet attr) throws BadLocationException { String str = strArg; if (str == null) { return; } if (docFormatter.isIncrementer() && !allowText && !isPartialOK && isAutoFmtOn) { return; } if (str.length() > 1) { if (!doSetText) { int len = Math.min(str.length(), docLimit); if (len < docLimit && docFields[len - 1].isIncrementer()) { return; } } if (okToInsertText(str)) { // This way truncates incoming values //int newLen = Math.min(str.length(), limit); //valState = offset + newLen < requiredLength; //super.insertString(offset, str.substring(0, newLen), attr); //valState = offset + str.length() < requiredLength ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; super.insertString(offset, str, attr); } else { //valState = UIValidatable.ErrorType.Error; //getToolkit().beep(); } validateState(); //System.out.println("******* "+(valState)); return; } int len = getLength() + str.length(); if (len <= docLimit) { UIFieldFormatterField field = docFields[offset]; if (!isCharOK(field, str)) { //getToolkit().beep(); //valState = UIValidatable.ErrorType.Error; //System.out.println("******* "+(valState)); validateState(); return; } if (field.getType() == UIFieldFormatterField.FieldType.separator) { if (str.charAt(0) != field.getValue().charAt(0)) { if (!isCharOK(docFields[offset + 1], str)) { //valState = UIValidatable.ErrorType.Error; //getToolkit().beep(); validateState(); return; } str = field.getValue() + str; } } else { //int offsetinx = offset; if (docFields[offset].isIncrementer()) { /*str = docFields[offset].getValue(); field = docFields[offset + docFields[offset].getValue().length()]; if (!isCharOK(field, str)) { //getToolkit().beep(); //valState = UIValidatable.ErrorType.Error; //System.out.println("******* "+(valState)); validateState(); return; }*/ } } //valState = offset + str.length() < requiredLength ? UIValidatable.ErrorType.Error : UIValidatable.ErrorType.Valid; super.insertString(offset, str, attr); String text = textField.getText(); if (text != null && text.length() < docLimit) { int inx = text.length(); field = docFields[inx]; if (field != null && (field.getType() == UIFieldFormatterField.FieldType.separator || field.isIncrementer())) { StringBuilder sb = new StringBuilder(text.substring(offset + str.length())); while (field != null && (field.getType() == UIFieldFormatterField.FieldType.separator || field.isIncrementer())) { if (field.getType() == UIFieldFormatterField.FieldType.separator) { sb.append(field.getValue()); inx++; } else { for (int i = 0; i < field.getSize(); i++) { sb.append("#"); } inx += field.getSize(); } if (inx < docLimit) { field = docFields[inx]; } else { field = null; } } insertString(offset + str.length(), sb.toString(), attr); } } } else { //valState = UIValidatable.ErrorType.Error; } validateState(); } } }