/* * RHQ Management Platform * Copyright (C) 2005-2011 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser 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 org.rhq.coregui.client.components.form; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.Set; import java.util.TreeSet; import com.smartgwt.client.widgets.form.DynamicForm; import com.smartgwt.client.widgets.form.fields.CanvasItem; import com.smartgwt.client.widgets.form.fields.FormItem; import com.smartgwt.client.widgets.form.fields.IntegerItem; import com.smartgwt.client.widgets.form.fields.SelectItem; import com.smartgwt.client.widgets.form.fields.StaticTextItem; import com.smartgwt.client.widgets.form.fields.events.ChangedEvent; import com.smartgwt.client.widgets.form.fields.events.ChangedHandler; import com.smartgwt.client.widgets.form.validator.IntegerRangeValidator; import org.rhq.core.domain.measurement.MeasurementUnits; import org.rhq.core.domain.measurement.composite.MeasurementNumericValueAndUnits; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.Messages; import org.rhq.coregui.client.util.FormUtility; import org.rhq.coregui.client.util.MeasurementConverterClient; import org.rhq.coregui.client.util.TypeConversionUtility; /** * A form item for entering a duration - consists of an IntegerItem for entering the amount of time and a * ComboBoxItem for entering the duration units. * * @author Ian Springer */ public class DurationItem extends CanvasItem { private static final Messages MSG = CoreGUI.getMessages(); private static final String FIELD_VALUE = "value"; private static final String FIELD_UNITS = "units"; private static final long SECOND_IN_MILLIS = 1000L; private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; private static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS; private static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS; private static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS; private static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS; private final DynamicForm form; private TimeUnit defaultTimeUnit; private Set<UnitType> supportedUnitTypes; private TimeUnit valueUnit; private boolean isReadOnly; private UnitType unitType; /** * @param name * @param title * @param supportedUnits when specified, the Set's most granular TimeUnit will be the valueUnit ({@link #getValueUnit()}) * @param supportsIterations * @param isReadOnly * @param parentWidget */ public DurationItem(String name, String title, TreeSet<TimeUnit> supportedUnits, boolean supportsIterations, boolean isReadOnly) { this(name, title, (null != supportedUnits && !supportedUnits.isEmpty()) ? supportedUnits.iterator().next() : null, supportedUnits, supportsIterations, isReadOnly); } /** * @param name * @param title * @param valueUnit the TimeUnit for to the item value ({@link #getValueUnit()}). If null the default is used. * If provided will override the default. The default is the supportedUnit Set's most granular TimeUnit ({@link #getValueUnit()}). * @param supportedUnits * @param supportsIterations * @param isReadOnly * @param parentWidget */ public DurationItem(String name, String title, TimeUnit valueUnit, TreeSet<TimeUnit> supportedUnits, boolean supportsIterations, boolean isReadOnly) { super(name, title); this.valueUnit = valueUnit; this.supportedUnitTypes = EnumSet.noneOf(UnitType.class); if (supportedUnits != null && !supportedUnits.isEmpty()) { this.supportedUnitTypes.add(UnitType.TIME); if (null == this.valueUnit) { this.valueUnit = supportedUnits.iterator().next(); } } if (supportsIterations) { this.supportedUnitTypes.add(UnitType.ITERATIONS); } this.isReadOnly = isReadOnly; this.form = new EnhancedDynamicForm(false, false); if (this.isReadOnly) { this.form.setNumCols(2); this.form.setColWidths("140", "160"); StaticTextItem staticTextItem = new StaticTextItem(FIELD_VALUE, title); staticTextItem.setShowTitle(getShowTitle()); this.form.setFields(staticTextItem); } else { this.form.setNumCols(4); this.form.setColWidths("140", "90", "105", "*"); final IntegerItem valueItem = new IntegerItem(FIELD_VALUE, title); valueItem.setShowTitle(getShowTitle()); valueItem.setValue(getValue()); IntegerRangeValidator integerRangeValidator = new IntegerRangeValidator(); integerRangeValidator.setMin(1); integerRangeValidator.setMax(Integer.MAX_VALUE); valueItem.setValidators(integerRangeValidator); valueItem.setValidateOnChange(true); valueItem.addChangedHandler(new ChangedHandler() { public void onChanged(ChangedEvent event) { updateValue(); } }); SelectItem unitsItem = new SelectItem(FIELD_UNITS); unitsItem.setShowTitle(false); LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>(); if (this.supportedUnitTypes.contains(UnitType.ITERATIONS)) { valueMap.put("times", MSG.common_unit_times()); } if (this.supportedUnitTypes.contains(UnitType.TIME)) { for (TimeUnit unit : supportedUnits) { valueMap.put(unit.name().toLowerCase(), unit.getDisplayName()); } } unitsItem.setValueMap(valueMap); if (this.defaultTimeUnit != null) { unitsItem.setDefaultValue(this.defaultTimeUnit.name().toLowerCase()); } else { unitsItem.setDefaultToFirstOption(true); } unitsItem.addChangedHandler(new ChangedHandler() { public void onChanged(ChangedEvent event) { updateValue(); } }); this.form.setFields(valueItem, unitsItem); valueItem.setWidth(90); unitsItem.setWidth(105); } setCanvas(this.form); } @Override public void setValidateOnChange(Boolean validateOnChange) { form.setValidateOnChange(validateOnChange); } @Override public void setValidateOnExit(Boolean validateOnExit) { form.setValidateOnChange(validateOnExit); } public void setValue(Integer value, UnitType unitType) { if (!this.supportedUnitTypes.contains(unitType)) { throw new IllegalArgumentException(MSG.widget_durationItem_unitTypeNotSupported(unitType.name())); } this.unitType = unitType; String unitString = null; switch (unitType) { case TIME: unitString = this.valueUnit.getDisplayName(); break; case ITERATIONS: unitString = MSG.common_unit_times(); } if (this.isReadOnly) { String stringValue; if (value == null) { stringValue = ""; } else { stringValue = value + " " + unitString; } this.form.setValue(FIELD_VALUE, stringValue); } else { if (value != null) { this.form.setValue(FIELD_VALUE, value); } else { this.form.setValue(FIELD_VALUE, (String) null); } this.form.setValue(FIELD_UNITS, unitString); } setValue(value); } /** * Sets human readable time representation to the form's field "value" * * @param longValue the time period representation in milliseconds */ public void setAndFormatValue(long longValue) { if (longValue < 0) { throw new IllegalArgumentException("negative time period " + longValue); } if (isReadOnly) { String formattedOutput = formatMilliseconds(longValue); this.unitType = UnitType.TIME; this.form.setValue(FIELD_VALUE, formattedOutput); setValue(formattedOutput); } else { MeasurementNumericValueAndUnits valueWithUnits; if (longValue % HOUR_IN_MILLIS == 0) { if (longValue == HOUR_IN_MILLIS) { valueWithUnits = new MeasurementNumericValueAndUnits(1.0d, MeasurementUnits.HOURS); } else { valueWithUnits = MeasurementConverterClient.fit((double) longValue, MeasurementUnits.MILLISECONDS, MeasurementUnits.HOURS, MeasurementUnits.HOURS); } } else if (longValue == MINUTE_IN_MILLIS) { valueWithUnits = new MeasurementNumericValueAndUnits(1.0d, MeasurementUnits.MINUTES); } else { valueWithUnits = MeasurementConverterClient.fit((double) longValue, MeasurementUnits.MILLISECONDS, MeasurementUnits.MINUTES, MeasurementUnits.MINUTES); } SelectItem unitsItem = (SelectItem) this.form.getItem(FIELD_UNITS); this.form.setValue(FIELD_VALUE, valueWithUnits.getValue().intValue()); unitsItem.setValue(valueWithUnits.getUnits().name().toLowerCase()); updateValue(); } } /** * formatMilliseconds(10 * WEEK_IN_MILLIS + 2 * SECOND_IN_MILLIS) = 2 months 2 weeks 2 seconds * * @param longValue * @return formatted string with time period representation */ private String formatMilliseconds(long longValue) { if (longValue < 0) { throw new IllegalArgumentException("negative time period " + longValue); } String formattedOutput = null; long wholeUnits = 0; if (longValue < SECOND_IN_MILLIS) { //ms return getTimeValue(longValue, TimeUnit.MILLISECONDS); } else if (longValue < MINUTE_IN_MILLIS) { //s wholeUnits = longValue / SECOND_IN_MILLIS; formattedOutput = getTimeValue(wholeUnits, TimeUnit.SECONDS); return formattedOutput + ((wholeUnits * SECOND_IN_MILLIS < longValue) ? " " + formatMilliseconds(longValue - wholeUnits * SECOND_IN_MILLIS) : ""); } else if (longValue < HOUR_IN_MILLIS) { //m wholeUnits = longValue / MINUTE_IN_MILLIS; formattedOutput = getTimeValue(wholeUnits, TimeUnit.MINUTES); return formattedOutput + ((wholeUnits * MINUTE_IN_MILLIS < longValue) ? " " + formatMilliseconds(longValue - wholeUnits * MINUTE_IN_MILLIS) : ""); } else if (longValue < DAY_IN_MILLIS) { //h wholeUnits = longValue / HOUR_IN_MILLIS; formattedOutput = getTimeValue(wholeUnits, TimeUnit.HOURS); return formattedOutput + ((wholeUnits * HOUR_IN_MILLIS < longValue) ? " " + formatMilliseconds(longValue - wholeUnits * HOUR_IN_MILLIS) : ""); } else if (longValue < WEEK_IN_MILLIS) { //d wholeUnits = longValue / DAY_IN_MILLIS; formattedOutput = getTimeValue(wholeUnits, TimeUnit.DAYS); return formattedOutput + ((wholeUnits * DAY_IN_MILLIS < longValue) ? " " + formatMilliseconds(longValue - wholeUnits * DAY_IN_MILLIS) : ""); } else if (longValue < MONTH_IN_MILLIS) { //w wholeUnits = longValue / WEEK_IN_MILLIS; formattedOutput = getTimeValue(wholeUnits, TimeUnit.WEEKS); return formattedOutput + ((wholeUnits * WEEK_IN_MILLIS < longValue) ? " " + formatMilliseconds(longValue - wholeUnits * WEEK_IN_MILLIS) : ""); } else if (longValue < YEAR_IN_MILLIS) { //M wholeUnits = longValue / MONTH_IN_MILLIS; formattedOutput = getTimeValue(wholeUnits, TimeUnit.MONTHS); return formattedOutput + ((wholeUnits * MONTH_IN_MILLIS < longValue) ? " " + formatMilliseconds(longValue - wholeUnits * MONTH_IN_MILLIS) : ""); } else if (longValue >= YEAR_IN_MILLIS) { //y wholeUnits = longValue / YEAR_IN_MILLIS; formattedOutput = getTimeValue(wholeUnits, TimeUnit.YEARS); return formattedOutput + ((wholeUnits * YEAR_IN_MILLIS < longValue) ? " " + formatMilliseconds(longValue - wholeUnits * YEAR_IN_MILLIS) : ""); } else { return ""; } } private String getTimeValue(long value, TimeUnit valueUnit) { return value + " " + valueUnit.getDisplayName(); } private void updateValue() { Long value = calculateValue(); setValue(value); } private Long calculateValue() { IntegerItem valueItem = (IntegerItem) this.form.getItem(FIELD_VALUE); Object value = valueItem.getValue(); Long integerValue = null; try { integerValue = TypeConversionUtility.toLong(value); } catch (NumberFormatException e) { return null; } Long convertedValue = null; if (integerValue != null) { TimeUnit unit = getInputTimeUnit(); if (unit == null) { this.unitType = UnitType.ITERATIONS; convertedValue = integerValue; } else { this.unitType = UnitType.TIME; if (unit.compareTo(this.valueUnit) < 0) { throw new IllegalStateException(MSG.widget_durationItem_inputUnitLessThanTargetUnit()); } switch (unit) { case MILLISECONDS: switch (this.valueUnit) { case MILLISECONDS: convertedValue = integerValue; break; } break; case SECONDS: switch (this.valueUnit) { case SECONDS: convertedValue = integerValue; break; case MILLISECONDS: convertedValue = integerValue * SECOND_IN_MILLIS; break; } break; case MINUTES: switch (this.valueUnit) { case MINUTES: convertedValue = integerValue; break; case SECONDS: convertedValue = integerValue * 60; break; case MILLISECONDS: convertedValue = integerValue * MINUTE_IN_MILLIS; break; } break; case HOURS: switch (this.valueUnit) { case HOURS: convertedValue = integerValue; break; case MINUTES: convertedValue = integerValue * 60; break; case SECONDS: convertedValue = integerValue * 60 * 60; break; case MILLISECONDS: convertedValue = integerValue * HOUR_IN_MILLIS; break; } break; case DAYS: switch (this.valueUnit) { case DAYS: convertedValue = integerValue; break; case HOURS: convertedValue = integerValue * 24; break; case MINUTES: convertedValue = integerValue * 24 * 60; break; case SECONDS: convertedValue = integerValue * 24 * 60 * 60; break; case MILLISECONDS: convertedValue = integerValue * DAY_IN_MILLIS; break; } break; case WEEKS: switch (this.valueUnit) { case WEEKS: convertedValue = integerValue; break; case DAYS: convertedValue = integerValue * 7; break; case HOURS: convertedValue = integerValue * 7 * 24; break; case MINUTES: convertedValue = integerValue * 7 * 24 * 60; break; case SECONDS: convertedValue = integerValue * 7 * 24 * 60 * 60; break; case MILLISECONDS: convertedValue = integerValue * WEEK_IN_MILLIS; break; } break; case MONTHS: switch (this.valueUnit) { case MONTHS: convertedValue = integerValue; break; case WEEKS: convertedValue = integerValue * 4; break; case DAYS: convertedValue = integerValue * 30; break; case HOURS: convertedValue = integerValue * 30 * 24; break; case MINUTES: convertedValue = integerValue * 30 * 24 * 60; break; case SECONDS: convertedValue = integerValue * 30 * 24 * 60 * 60; break; case MILLISECONDS: convertedValue = integerValue * MONTH_IN_MILLIS; break; } break; case YEARS: switch (this.valueUnit) { case YEARS: convertedValue = integerValue; break; case MONTHS: convertedValue = integerValue * 12; break; case WEEKS: convertedValue = integerValue * 52; break; case DAYS: convertedValue = integerValue * 365; break; case HOURS: convertedValue = integerValue * 365 * 24; break; case MINUTES: convertedValue = integerValue * 365 * 24 * 60; break; case SECONDS: convertedValue = integerValue * 365 * 24 * 60 * 60; break; case MILLISECONDS: convertedValue = integerValue * YEAR_IN_MILLIS; break; } break; } } } return convertedValue; } private TimeUnit getInputTimeUnit() { SelectItem unitsItem = (SelectItem) this.form.getItem(FIELD_UNITS); String unitString = unitsItem.getValueAsString(); // this will always be non-null TimeUnit unit; try { unit = TimeUnit.valueOf(unitString.toUpperCase()); } catch (IllegalArgumentException e) { // not a time unit, so unit must be "times" (i.e. iterations) unit = null; } return unit; } public UnitType getUnitType() { return unitType; } public Integer getValueAsInteger() { return TypeConversionUtility.toInteger(getValue()); } public Long getValueAsLong() { return TypeConversionUtility.toLong(getValue()); } @Override public Boolean validate() { return this.form.validate(); } public void setDefaultTimeUnit(TimeUnit defaultTimeUnit) { this.defaultTimeUnit = defaultTimeUnit; } public void setContextualHelp(String contextualHelp) { if (contextualHelp != null) { FormItem item; if (this.isReadOnly) { item = this.form.getItem(FIELD_VALUE); } else { item = this.form.getItem(FIELD_UNITS); } FormUtility.addContextualHelp(item, contextualHelp); } } /** * If this item's {@link #getValue() value} represents a time duration, returns the value's time unit; otherwise * returns null. * * @return if this item's {@link #getValue() value} represents a time duration, returns the value's time unit; otherwise * returns null */ public TimeUnit getValueUnit() { return this.valueUnit; } @Override public String toString() { Object value = form.getValue(FIELD_VALUE); String string; if (value != null) { TimeUnit timeUnit = getInputTimeUnit(); String unitString = (timeUnit != null) ? timeUnit.name().toLowerCase() : MSG.common_unit_times(); string = value + " " + unitString; } else { string = ""; } return string; } }