package org.sigmah.shared.dto.element;
/*
* #%L
* Sigmah
* %%
* Copyright (C) 2010 - 2016 URD
* %%
* 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 3 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/gpl-3.0.html>.
* #L%
*/
import com.extjs.gxt.ui.client.widget.form.TextArea;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.DatePickerEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.form.ComboBox;
import com.extjs.gxt.ui.client.widget.form.DateField;
import com.extjs.gxt.ui.client.widget.form.Field;
import com.extjs.gxt.ui.client.widget.form.LabelField;
import com.extjs.gxt.ui.client.widget.form.NumberField;
import com.extjs.gxt.ui.client.widget.form.TextField;
import java.util.Date;
import java.util.List;
import org.sigmah.client.dispatch.CommandResultHandler;
import org.sigmah.client.i18n.I18N;
import org.sigmah.client.util.DateUtils;
import org.sigmah.shared.command.GetCountries;
import org.sigmah.shared.command.GetCountry;
import org.sigmah.shared.command.result.ListResult;
import org.sigmah.shared.dto.country.CountryDTO;
import org.sigmah.shared.dto.element.event.RequiredValueEvent;
import org.sigmah.shared.dto.element.event.ValueEvent;
import org.sigmah.shared.dto.orgunit.OrgUnitDTO;
import com.allen_sauer.gwt.log.client.Log;
public abstract class AbstractDefaultFlexibleElementDTO extends FlexibleElementDTO {
protected static final String EMPTY_VALUE = "-";
protected transient ListStore<CountryDTO> countriesStore;
protected transient ListStore<OrgUnitDTO> orgUnitsStore;
protected void fireEvents(String value, boolean isValueOn) {
Log.debug("raw Value is : " + value + " isValueOn is :" + isValueOn);
handlerManager.fireEvent(new ValueEvent(this, value));
// Required element ?
if (getValidates()) {
handlerManager.fireEvent(new RequiredValueEvent(isValueOn));
}
}
/**
* Create a text field to represent a default flexible element.
*
* @param length The max length of the field.
* @param allowBlank If the field allow blank value.
* @return The text field.
*/
protected TextField<String> createStringField(final int length, final boolean allowBlank) {
final TextField<String> textField = new TextField<String>();
textField.setAllowBlank(allowBlank);
// Sets the max length.
textField.setMaxLength(length);
// Adds the listeners.
textField.addListener(Events.OnKeyUp, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
String rawValue = textField.getValue();
if (rawValue == null) {
rawValue = "";
}
// The value is valid if it contains at least one non-blank
// character.
final boolean isValueOn = !rawValue.trim().equals("") && !(rawValue.length() > length);
if (!(!allowBlank && !isValueOn)) {
fireEvents(rawValue, isValueOn);
}
}
});
return textField;
}
/**
* Create a date field to represent a default flexible element.
*
* @param allowBlank If the field allow blank value.
* @return The date field.
*/
protected DateField createDateField(final boolean allowBlank) {
final DateTimeFormat dateFormat = DateUtils.DATE_SHORT;
// Creates a date field which manages date picker selections and
// manual selections.
final DateField dateField = new DateField();
dateField.getPropertyEditor().setFormat(dateFormat);
dateField.setEditable(allowBlank);
dateField.setAllowBlank(allowBlank);
preferredWidth = FlexibleElementDTO.NUMBER_FIELD_WIDTH;
// Adds the listeners.
dateField.getDatePicker().addListener(Events.Select, new Listener<DatePickerEvent>() {
@Override
public void handleEvent(DatePickerEvent be) {
// The date is saved as a timestamp.
final String rawValue = String.valueOf(be.getDate().getTime());
// The date picker always returns a valid date.
final boolean isValueOn = true;
fireEvents(rawValue, isValueOn);
}
});
dateField.addListener(Events.OnKeyUp, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
final Date date = dateField.getValue();
// The date is invalid, fires only a required event to
// invalidate some previously valid date.
if (date == null) {
// Required element ?
if (getValidates()) {
handlerManager.fireEvent(new RequiredValueEvent(false));
}
if (allowBlank) {
fireEvents("", false);
}
return;
}
// The date is saved as a timestamp.
final String rawValue = String.valueOf(date.getTime());
// The date is valid here.
final boolean isValueOn = true;
if (!(!allowBlank && !isValueOn)) {
fireEvents(rawValue, isValueOn);
}
}
});
return dateField;
}
/**
* Create a number field to represent a default flexible element.
*
* @param allowBlank If the field allow blank value.
* @return The number field.
*/
protected NumberField createNumberField(final boolean allowBlank) {
final NumberField numberField = new NumberField();
numberField.setAllowDecimals(true);
numberField.setAllowNegative(false);
numberField.setAllowBlank(allowBlank);
preferredWidth = FlexibleElementDTO.NUMBER_FIELD_WIDTH;
// Decimal format
final NumberFormat format = NumberFormat.getDecimalFormat();
numberField.setFormat(format);
// Sets the min value.
final Number minValue = 0.0;
numberField.setMinValue(minValue);
return numberField;
}
/**
* Create a label field to represent a default flexible element.
*
* @return The label field.
*/
protected LabelField createLabelField() {
final LabelField labelField = new LabelField();
labelField.setLabelSeparator(":");
return labelField;
}
/**
* Create a label field and sets its value.
*
* @param value Value to set.
* @return The label field.
*/
protected LabelField createLabelField(String value) {
final LabelField labelField = createLabelField();
labelField.setValue(value);
return labelField;
}
/**
* Creates a text field. <br/>
* This method is shared between the code and the title fields.
*
* @param label Label of the field.
* @param value Current value.
* @param size Maximum number of characters allowed.
* @param enabled <code>true</code> if the field must be editable, <code>false</code> otherwise.
* @return A new text field.
*/
protected Field<?> buildTextField(String label, String value, int size, boolean enabled, boolean allowBlank) {
final Field<?> field;
// Builds the field and sets its value.
if (enabled) {
final TextField<String> textField = createStringField(size, allowBlank);
textField.setValue(value);
field = textField;
} else {
field = createLabelField(value);
}
// Sets the field label.
setLabel(label);
field.setFieldLabel(getLabel());
return field;
}
protected Field<?> buildParagraphField(final String label, final String value, final int size, final boolean enabled, boolean allowBlank) {
// Builds the field and sets its value.
final TextField<String> textArea = new TextArea();
textArea.addStyleName("flexibility-textarea");
textArea.setAllowBlank(allowBlank);
// Sets the max length.
textArea.setMaxLength(size);
textArea.setToolTip(I18N.MESSAGES.flexibleElementTextAreaTextLength(String.valueOf(size)));
// Adds the listeners.
textArea.addListener(Events.OnKeyUp, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
String rawValue = textArea.getValue();
if (rawValue == null) {
rawValue = "";
}
// The value is valid if it contains at least one
// non-blank character.
final boolean isValueOn = !rawValue.trim().equals("") && !(rawValue.length() > size);
fireEvents(rawValue, isValueOn);
}
});
// Sets the value to the field.
if (value != null) {
textArea.setValue(value);
}
setLabel(label);
textArea.setFieldLabel(getLabel());
textArea.setEnabled(enabled);
return textArea;
}
/**
* Creates a date field. <br/>
* This method is shared between the start date and the end date fields.
*
* @param label Label of the field.
* @param value Current value.
* @param enabled <code>true</code> if the field must be editable, <code>false</code> otherwise.
* @return A new date field.
*/
protected Field<?> buildDateField(String label, Date value, boolean enabled) {
final Field<?> field;
// Builds the field and sets its value.
if (enabled) {
final DateField dateField = createDateField(true);
dateField.setValue(value);
field = dateField;
} else {
final LabelField labelField = createLabelField();
if (value != null) {
labelField.setValue(DateUtils.DATE_SHORT.format(value));
} else {
labelField.setValue(EMPTY_VALUE);
}
field = labelField;
}
// Sets the field label.
setLabel(label);
field.setFieldLabel(getLabel());
return field;
}
protected Component buildCountryField(String country, boolean enabled) {
final Field<?> field = buildCountryField((CountryDTO) null, enabled);
final int countryId = Integer.parseInt(country);
dispatch.execute(new GetCountry(countryId), new CommandResultHandler<CountryDTO>() {
@Override
public void onCommandFailure(final Throwable caught) {
}
@Override
public void onCommandSuccess(final CountryDTO result) {
// BUGFIX #694: Disable events on first set.
field.enableEvents(false);
if (field instanceof ComboBox) {
((ComboBox<CountryDTO>) field).setValue(result);
} else if (field instanceof LabelField) {
((LabelField) field).setValue(result.getName());
}
field.enableEvents(true);
}
});
return field;
}
protected Field<?> buildCountryField(CountryDTO country, boolean enabled) {
final Field<?> field;
if (enabled) {
final ComboBox<CountryDTO> comboBox = new ComboBox<CountryDTO>();
comboBox.setEmptyText(I18N.CONSTANTS.flexibleElementDefaultSelectCountry());
ensureCountryStore();
comboBox.setStore(countriesStore);
comboBox.setDisplayField(CountryDTO.NAME);
comboBox.setValueField(CountryDTO.ID);
comboBox.setTriggerAction(ComboBox.TriggerAction.ALL);
comboBox.setEditable(true);
comboBox.setAllowBlank(true);
// Listens to the selection changes.
comboBox.addSelectionChangedListener(new SelectionChangedListener<CountryDTO>() {
@Override
public void selectionChanged(SelectionChangedEvent<CountryDTO> se) {
String value = null;
final boolean isValueOn;
// Gets the selected choice.
final CountryDTO choice = se.getSelectedItem();
// Checks if the choice isn't the default empty choice.
isValueOn = choice != null && choice.getId() != null && choice.getId() != -1;
if (choice != null) {
value = String.valueOf(choice.getId());
}
if (value != null) {
// Fires value change event.
handlerManager.fireEvent(new ValueEvent(AbstractDefaultFlexibleElementDTO.this, value));
}
// Required element ?
if (getValidates()) {
handlerManager.fireEvent(new RequiredValueEvent(isValueOn));
}
}
});
if (country != null) {
comboBox.setValue(country);
}
field = comboBox;
} else /* not enabled */ {
final LabelField labelField = createLabelField();
if (country == null) {
labelField.setValue(EMPTY_VALUE);
} else {
labelField.setValue(country.getName());
}
field = labelField;
}
// Sets the field label.
setLabel(I18N.CONSTANTS.projectCountry());
field.setFieldLabel(getLabel());
return field;
}
/**
* Creates the organization unit field.
*
* @param orgUnitId ID of the organization unit.
* @param enabled <code>true</code> if the field must be editable, <code>false</code> otherwise.
* @return The organization unit field.
*/
protected Field<?> buildOrgUnitField(String label, String orgUnitId, boolean enabled) {
return buildOrgUnitField(label, Integer.parseInt(orgUnitId), enabled);
}
/**
* Creates the organization unit field.
*
* @param label Label of the field
* @param orgUnitId ID of the organization unit.
* @param enabled <code>true</code> if the field must be editable, <code>false</code> otherwise.
* @return The organization unit field.
*/
protected Field<?> buildOrgUnitField(String label, Integer orgUnitId, boolean enabled) {
final Field<?> field;
if (enabled) {
final ComboBox<OrgUnitDTO> comboBox = new ComboBox<OrgUnitDTO>();
ensureOrgUnitStore();
comboBox.setStore(orgUnitsStore);
comboBox.setDisplayField(OrgUnitDTO.COMPLETE_NAME);
comboBox.setValueField(OrgUnitDTO.ID);
comboBox.setTriggerAction(ComboBox.TriggerAction.ALL);
comboBox.setEditable(true);
comboBox.setAllowBlank(true);
// BUGFIX #694 : SelectionChangedEvent listener is added AFTER
// setting the initial value to avoid sending a
// SelectionChangedEvent during view initialization.
// Loading the current value from the cache.
cache.getOrganizationCache().get(orgUnitId, new AsyncCallback<OrgUnitDTO>() {
@Override
public void onFailure(final Throwable caught) {
// Not found.
// Listens to the selection changes.
addOrgUnitSelectionChangedListener(comboBox);
}
@Override
public void onSuccess(final OrgUnitDTO result) {
comboBox.setValue(result);
// Listens to the selection changes.
addOrgUnitSelectionChangedListener(comboBox);
}
});
field = comboBox;
} else {
// Builds the field and sets its value.
final LabelField labelField = createLabelField();
cache.getOrganizationCache().get(orgUnitId, new AsyncCallback<OrgUnitDTO>() {
@Override
public void onSuccess(final OrgUnitDTO result) {
// BUGFIX: Issue #718
if(result != null) {
labelField.setValue(result.getName() + " - " + result.getFullName());
} else {
labelField.setValue(EMPTY_VALUE);
}
}
@Override
public void onFailure(final Throwable caught) {
labelField.setValue(EMPTY_VALUE);
}
});
field = labelField;
}
// Sets the field label.
setLabel(label);
field.setFieldLabel(getLabel());
return field;
}
/**
* Creates the organization unit field.
*
* @param orgUnit Organization unit.
* @param enabled <code>true</code> if the field must be editable, <code>false</code> otherwise.
* @return The organization unit field.
*/
protected Field<?> buildOrgUnitField(String label, OrgUnitDTO orgUnit, boolean enabled) {
final Field<?> field;
if (enabled) {
final ComboBox<OrgUnitDTO> comboBox = new ComboBox<OrgUnitDTO>();
ensureOrgUnitStore();
comboBox.setStore(orgUnitsStore);
comboBox.setDisplayField(OrgUnitDTO.COMPLETE_NAME);
comboBox.setValueField(OrgUnitDTO.ID);
comboBox.setTriggerAction(ComboBox.TriggerAction.ALL);
comboBox.setEditable(true);
comboBox.setAllowBlank(true);
// BUGFIX #694 : SelectionChangedEvent listener is added AFTER
// setting the initial value to avoid sending a
// SelectionChangedEvent during view initialization.
comboBox.setValue(orgUnit);
// Listens to the selection changes.
addOrgUnitSelectionChangedListener(comboBox);
field = comboBox;
} else {
// Builds the field and sets its value.
final LabelField labelField = createLabelField();
if(orgUnit != null) {
labelField.setValue(orgUnit.getName() + " - " + orgUnit.getFullName());
} else {
labelField.setValue(EMPTY_VALUE);
}
field = labelField;
}
// Sets the field label.
setLabel(label);
field.setFieldLabel(getLabel());
return field;
}
protected String formatCountry(String value) {
if (cache != null) {
try {
final CountryDTO c = cache.getCountryCache().get(Integer.valueOf(value));
if (c != null) {
return c.getName();
} else {
return '#' + value;
}
} catch (NumberFormatException e) {
return "";
}
} else {
return '#' + value;
}
}
protected String formatDate(String value) {
try {
final long time = Long.parseLong(value);
final Date date = new Date(time);
// Using a shared instance to allow parsing from client and server side.
final com.google.gwt.i18n.shared.DateTimeFormat formatter = DateUtils.SHARED_DATE_SHORT;
return formatter.format(date);
} catch (NumberFormatException e) {
return "";
}
}
protected String formatText(String value) {
return value.replace("\n", "<br>");
}
protected String formatOrgUnit(String value) {
if (cache != null) {
try {
final OrgUnitDTO o = cache.getOrganizationCache().get(Integer.valueOf(value));
if (o != null) {
return o.getName() + " - " + o.getFullName();
} else {
return '#' + value;
}
} catch(NumberFormatException e) {
return "";
}
} else {
return '#' + value;
}
}
/**
* Creates and populates the shared country store if needed.
*/
protected void ensureCountryStore() {
if (countriesStore == null) {
countriesStore = new ListStore<CountryDTO>();
}
// if country store is empty
if (countriesStore.getCount() == 0) {
if (cache != null) {
cache.getCountryCache().get(new AsyncCallback<List<CountryDTO>>() {
@Override
public void onFailure(Throwable e) {
Log.error("[getComponent] Error while getting countries list.", e);
}
@Override
public void onSuccess(List<CountryDTO> result) {
// Fills the store.
countriesStore.add(result);
}
});
} else /* cache is null */ {
dispatch.execute(new GetCountries(CountryDTO.Mode.BASE), new CommandResultHandler<ListResult<CountryDTO>>() {
@Override
protected void onCommandSuccess(final ListResult<CountryDTO> result) {
// Fills the store.
countriesStore.add(result.getData());
}
@Override
protected void onCommandFailure(final Throwable caught) {
Log.error("[getComponent] Error while getting countries list.", caught);
}
});
}
}
}
/**
* Creates and populates the shared org unit store if needed.
*/
protected void ensureOrgUnitStore() {
if (orgUnitsStore == null) {
orgUnitsStore = new ListStore<OrgUnitDTO>();
}
if(orgUnitsStore.getCount() == 0) {
cache.getOrganizationCache().get(new AsyncCallback<OrgUnitDTO>() {
@Override
public void onFailure(Throwable e) {
Log.error("[getComponent] Error while getting users info.", e);
}
@Override
public void onSuccess(OrgUnitDTO result) {
// Fills the store.
recursiveFillOrgUnitsList(result);
}
});
}
}
/**
* Fills recursively the org unit store from the given root org unit.
*
* @param root
* The root org unit.
*/
private void recursiveFillOrgUnitsList(OrgUnitDTO root) {
if (root.isCanContainProjects()) {
orgUnitsStore.add(root);
}
for (final OrgUnitDTO child : root.getChildrenOrgUnits()) {
recursiveFillOrgUnitsList(child);
}
}
/**
* Adds the selection changed listener to the given orgunit combobox.
*
* @param comboBox Combo box to configure.
*/
protected abstract void addOrgUnitSelectionChangedListener(final ComboBox<OrgUnitDTO> comboBox);
}