/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.cids.editors; import Sirius.navigator.tools.CacheException; import Sirius.navigator.tools.MetaObjectCache; import Sirius.navigator.tools.MetaObjectChangeEvent; import Sirius.navigator.tools.MetaObjectChangeListener; import Sirius.navigator.tools.MetaObjectChangeSupport; import Sirius.server.localserver.attribute.ClassAttribute; import Sirius.server.middleware.types.MetaClass; import Sirius.server.middleware.types.MetaClassStore; import Sirius.server.middleware.types.MetaObject; import org.apache.log4j.Logger; import org.jdesktop.beansbinding.Converter; import org.jdesktop.beansbinding.Validator; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; import java.awt.Component; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListCellRenderer; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.SwingWorker; import de.cismet.cids.dynamics.CidsBean; import de.cismet.tools.CismetThreadPool; /** * DOCUMENT ME! * * @author thorsten * @version $Revision$, $Date$ */ public class DefaultBindableReferenceCombo extends JComboBox implements Bindable, MetaClassStore, Serializable { //~ Static fields/initializers --------------------------------------------- private static final Logger LOG = Logger.getLogger(DefaultBindableReferenceCombo.class); protected static final Comparator<CidsBean> BEAN_TOSTRING_COMPARATOR = new BeanToStringComparator(); //~ Instance fields -------------------------------------------------------- protected CidsBean cidsBean; private final transient MetaObjectChangeListener mocL; private MetaClass metaClass; private boolean fakeModel; private boolean nullable; private boolean onlyUsed; private Comparator<CidsBean> comparator; private String nullValueRepresentation; private String sortingColumn; //~ Constructors ----------------------------------------------------------- /** * Creates a new DefaultBindableReferenceCombo object. */ public DefaultBindableReferenceCombo() { this(null, false, false, BEAN_TOSTRING_COMPARATOR); } /** * Creates a new DefaultBindableReferenceCombo object. * * @param nullable DOCUMENT ME! */ public DefaultBindableReferenceCombo(final boolean nullable) { this(null, nullable, false, BEAN_TOSTRING_COMPARATOR); } /** * Creates a new DefaultBindableReferenceCombo object. * * @param comparator DOCUMENT ME! */ public DefaultBindableReferenceCombo(final Comparator<CidsBean> comparator) { this(null, false, false, comparator); } /** * Creates a new DefaultBindableReferenceCombo object. * * @param mc DOCUMENT ME! */ public DefaultBindableReferenceCombo(final MetaClass mc) { this(mc, false, false, BEAN_TOSTRING_COMPARATOR); } /** * Creates a new DefaultBindableReferenceCombo object. * * @param mc DOCUMENT ME! * @param sortingcolumn DOCUMENT ME! */ public DefaultBindableReferenceCombo(final MetaClass mc, final String sortingcolumn) { this(mc, false, false, BEAN_TOSTRING_COMPARATOR, sortingcolumn); } /** * Creates a new DefaultBindableReferenceCombo object. * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * @param onlyUsed DOCUMENT ME! */ public DefaultBindableReferenceCombo(final MetaClass mc, final boolean nullable, final boolean onlyUsed) { this(mc, nullable, onlyUsed, BEAN_TOSTRING_COMPARATOR); } /** * Creates a new DefaultBindableReferenceCombo object. * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * @param onlyUsed DOCUMENT ME! * @param comparator DOCUMENT ME! */ public DefaultBindableReferenceCombo(final MetaClass mc, final boolean nullable, final boolean onlyUsed, final Comparator<CidsBean> comparator) { this(mc, nullable, onlyUsed, BEAN_TOSTRING_COMPARATOR, null); } /** * Creates a new DefaultBindableReferenceCombo object. * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * @param onlyUsed DOCUMENT ME! * @param comparator DOCUMENT ME! * @param sortingColumn DOCUMENT ME! */ public DefaultBindableReferenceCombo(final MetaClass mc, final boolean nullable, final boolean onlyUsed, final Comparator<CidsBean> comparator, final String sortingColumn) { final String[] s = new String[] { NbBundle.getMessage(DefaultBindableReferenceCombo.class, "DefaultBindableReferenceCombo.loading") }; setModel(new DefaultComboBoxModel(s)); this.nullable = nullable; this.onlyUsed = onlyUsed; this.comparator = comparator; this.nullValueRepresentation = " "; this.sortingColumn = sortingColumn; this.mocL = new MetaObjectChangeListenerImpl(); this.setRenderer(new DefaultBindableReferenceComboRenderer()); init(mc, false); final MetaObjectChangeSupport mocSupport = MetaObjectChangeSupport.getDefault(); mocSupport.addMetaObjectChangeListener(WeakListeners.create(MetaObjectChangeListener.class, mocL, mocSupport)); } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @param sortingColumn DOCUMENT ME! */ public void setSortingColumn(final String sortingColumn) { this.sortingColumn = sortingColumn; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public String getSortingColumn() { return sortingColumn; } /** * DOCUMENT ME! * * @param mc DOCUMENT ME! * @param forceReload DOCUMENT ME! */ protected void init(final MetaClass mc, final boolean forceReload) { if (!isFakeModel() && (mc != null)) { CismetThreadPool.execute(new SwingWorker<DefaultComboBoxModel, Void>() { @Override protected DefaultComboBoxModel doInBackground() throws Exception { Thread.currentThread().setName("DefaultBindableReferenceCombo init()"); return getModelByMetaClass(mc, nullable, onlyUsed, comparator, forceReload, sortingColumn); } @Override protected void done() { try { final DefaultComboBoxModel tmp = get(); tmp.setSelectedItem(cidsBean); setModel(tmp); } catch (InterruptedException interruptedException) { } catch (ExecutionException executionException) { LOG.error("Error while initializing the model of a referenceCombo", executionException); // NOI18N } } }); } } /** * DOCUMENT ME! * * @param forceReload DOCUMENT ME! * * @throws IllegalStateException DOCUMENT ME! */ public void reload(final boolean forceReload) { if (metaClass == null) { throw new IllegalStateException("the metaclass has not been set yet"); // NOI18N } init(metaClass, forceReload); } @Override public String getBindingProperty() { return "selectedItem"; // NOI18N } @Override public Validator getValidator() { return null; } @Override public Converter getConverter() { return null; } @Override public void setSelectedItem(Object anObject) { if (isFakeModel()) { setModel(new DefaultComboBoxModel(new Object[] { anObject })); } if (!(anObject instanceof CidsBean)) { anObject = null; } super.setSelectedItem(anObject); cidsBean = (CidsBean)anObject; } @Override public MetaClass getMetaClass() { return metaClass; } @Override public void setMetaClass(final MetaClass metaClass) { this.metaClass = metaClass; init(metaClass, false); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isFakeModel() { return fakeModel; } /** * DOCUMENT ME! * * @param fakeModel DOCUMENT ME! */ public void setFakeModel(final boolean fakeModel) { this.fakeModel = fakeModel; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public String getNullValueRepresentation() { return nullValueRepresentation; } /** * DOCUMENT ME! * * @param nullValueRepresentation DOCUMENT ME! */ public void setNullValueRepresentation(final String nullValueRepresentation) { this.nullValueRepresentation = nullValueRepresentation; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isNullable() { return nullable; } /** * DOCUMENT ME! * * @param onlyUsed DOCUMENT ME! */ public void setOnlyUsed(final boolean onlyUsed) { this.onlyUsed = onlyUsed; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isOnlyUsed() { return onlyUsed; } /** * DOCUMENT ME! * * @param nullable DOCUMENT ME! */ public void setNullable(final boolean nullable) { this.nullable = nullable; } @Override public Object getNullSourceValue() { return null; } @Override public Object getErrorSourceValue() { return null; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ protected Comparator<CidsBean> getComparator() { return comparator; } /** * DOCUMENT ME! * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * @param onlyUsed DOCUMENT ME! * @param comparator DOCUMENT ME! * @param forceReload DOCUMENT ME! * * @return DOCUMENT ME! */ public static DefaultComboBoxModel getModelByMetaClass(final MetaClass mc, final boolean nullable, final boolean onlyUsed, final Comparator<CidsBean> comparator, final boolean forceReload) { return getModelByMetaClass(mc, nullable, onlyUsed, comparator, forceReload, null); } /** * DOCUMENT ME! * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * @param onlyUsed DOCUMENT ME! * @param comparator DOCUMENT ME! * @param forceReload DOCUMENT ME! * @param sortingColumn DOCUMENT ME! * * @return DOCUMENT ME! */ public static DefaultComboBoxModel getModelByMetaClass(final MetaClass mc, final boolean nullable, final boolean onlyUsed, final Comparator<CidsBean> comparator, final boolean forceReload, final String sortingColumn) { if (mc != null) { final ClassAttribute ca = mc.getClassAttribute("sortingColumn"); // NOI18N final String orderBy; // NOI18N if (sortingColumn == null) { if (ca != null) { final String value = ca.getValue().toString(); orderBy = " order by " + value; // NOI18N } else { orderBy = ""; } } else { orderBy = " order by " + sortingColumn; } String query = "select " + mc.getID() + "," + mc.getPrimaryKey() + " from " + mc.getTableName(); // NOI18N if (onlyUsed) { query += " where used is true"; // NOI18N } query += orderBy; MetaObject[] metaObjects; try { metaObjects = MetaObjectCache.getInstance().getMetaObjectsByQuery(query, mc.getDomain(), forceReload); } catch (final CacheException ex) { LOG.warn("cache could not come up with appropriate objects", ex); // NOI18N metaObjects = new MetaObject[0]; } final List<CidsBean> cbv = new ArrayList<CidsBean>(metaObjects.length); if (nullable) { cbv.add(null); } for (final MetaObject mo : metaObjects) { cbv.add(mo.getBean()); } if (orderBy.length() == 0) { // Sorts the model using String comparison on the bean's toString() Collections.sort(cbv, comparator); } return new DefaultComboBoxModel(cbv.toArray()); } else { return new DefaultComboBoxModel(); } } /** * DOCUMENT ME! * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * @param onlyUsed DOCUMENT ME! * @param comparator DOCUMENT ME! * * @return DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ public static DefaultComboBoxModel getModelByMetaClass(final MetaClass mc, final boolean nullable, final boolean onlyUsed, final Comparator<CidsBean> comparator) throws Exception { return getModelByMetaClass(mc, nullable, onlyUsed, comparator, false); } /** * DOCUMENT ME! * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * @param onlyUsed DOCUMENT ME! * * @return DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ public static DefaultComboBoxModel getModelByMetaClass(final MetaClass mc, final boolean nullable, final boolean onlyUsed) throws Exception { return getModelByMetaClass(mc, nullable, onlyUsed, BEAN_TOSTRING_COMPARATOR, false); } /** * DOCUMENT ME! * * @param mc DOCUMENT ME! * @param nullable DOCUMENT ME! * * @return DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ public static DefaultComboBoxModel getModelByMetaClass(final MetaClass mc, final boolean nullable) throws Exception { return getModelByMetaClass(mc, nullable, false, BEAN_TOSTRING_COMPARATOR, false); } //~ Inner Classes ---------------------------------------------------------- /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ protected static final class BeanToStringComparator implements Comparator<CidsBean> { //~ Methods ------------------------------------------------------------ @Override public int compare(final CidsBean o1, final CidsBean o2) { final String s1 = (o1 == null) ? "" : o1.toString(); // NOI18N final String s2 = (o2 == null) ? "" : o2.toString(); // NOI18N return (s1).compareToIgnoreCase(s2); } } /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ private final class MetaObjectChangeListenerImpl implements MetaObjectChangeListener { //~ Methods ------------------------------------------------------------ @Override public void metaObjectAdded(final MetaObjectChangeEvent moce) { // we're only registered to the DefaultMetaObjectChangeSupport that asserts of proper initialisation of // events if ((metaClass != null) && metaClass.equals(moce.getNewMetaObject().getMetaClass())) { init(metaClass, true); } } @Override public void metaObjectChanged(final MetaObjectChangeEvent moce) { // we're only registered to the DefaultMetaObjectChangeSupport that asserts of proper initialisation of // events if ((metaClass != null) && metaClass.equals(moce.getNewMetaObject().getMetaClass())) { init(metaClass, true); } } @Override public void metaObjectRemoved(final MetaObjectChangeEvent moce) { // we're only registered to the DefaultMetaObjectChangeSupport that asserts of proper initialisation of // events if ((metaClass != null) && metaClass.equals(moce.getOldMetaObject().getMetaClass())) { init(metaClass, true); } } } /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ private final class DefaultBindableReferenceComboRenderer extends DefaultListCellRenderer { //~ Methods ------------------------------------------------------------ @Override public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) { final Component ret = super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus); if ((value == null) && (ret instanceof JLabel)) { ((JLabel)ret).setText(getNullValueRepresentation()); } return ret; } } }