package fr.lteconsulting.hexa.client.ui.search; import java.util.ArrayList; import java.util.HashMap; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONString; import com.google.gwt.json.client.JSONValue; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import fr.lteconsulting.hexa.client.common.Pair; import fr.lteconsulting.hexa.client.css.HexaCss; import fr.lteconsulting.hexa.client.ui.ListTable; import fr.lteconsulting.hexa.client.ui.dialog.MyPopupPanel; import fr.lteconsulting.hexa.client.ui.search.CriteriaSwitch.XCriteriaSwitch; import fr.lteconsulting.hexa.client.ui.tree.ILeafWidget; import fr.lteconsulting.hexa.client.ui.tree.INodeWidget; import fr.lteconsulting.hexa.client.ui.tree.INodeWidget.XNodeWidget; import fr.lteconsulting.hexa.client.ui.tree.LeafWidget; import fr.lteconsulting.hexa.client.ui.tree.NodeWidget; import fr.lteconsulting.hexa.client.ui.tree.NodeWidget.INodeWidgetFactory; import fr.lteconsulting.hexa.client.ui.tree.TreeWidget; public class StandardSearchMng implements SearchMng { interface Css extends HexaCss { public static final Css CSS = GWT.create( Css.class ); String anchorButton(); } public static JSONObject wrapJson( JSONValue json ) { JSONObject obj = new JSONObject(); obj.put( "v", new JSONString( "v2" ) ); obj.put( "data", json ); return obj; } public static JSONValue unwrapJson( JSONValue json ) { if( json == null ) return null; JSONObject obj = json.isObject(); if( obj == null ) return null; JSONValue v = obj.get( "v" ); if( v == null ) return null; JSONString vs = v.isString(); if( vs == null ) return null; if( !vs.stringValue().equals( "v2" ) ) return null; return obj.get( "data" ); } String name; ArrayList<Pair<String, String>> standardOperators = new ArrayList<Pair<String, String>>(); ArrayList<String> standardOperatorIds = new ArrayList<String>(); ArrayList<ICriteriaMng> criteriaMngList = new ArrayList<ICriteriaMng>(); HashMap<String, ICriteriaMng> standardPointingFieldCriteriaMngs = new HashMap<String, ICriteriaMng>(); HashMap<String, ICriteriaMng> standardPointedByCriteriaMngs = new HashMap<String, ICriteriaMng>(); HashMap<ICriteriaMng, Integer> alignments = new HashMap<ICriteriaMng, Integer>(); HashMap<String, ICriteriaMng> fieldNameToCriteriaMngs = new HashMap<String, ICriteriaMng>(); public void addStandardCombineOperator( String displayName, String id ) { standardOperators.add( new Pair<String, String>( id, displayName ) ); standardOperatorIds.add( id ); } public void addStandardCriteriaMng( String displayName, String fieldName, ArrayList<Pair<String, String>> comparators, ValueUIFactory factory ) { ICriteriaMng mng = new StandardCriteriaMng( displayName, fieldName, comparators, factory ); criteriaMngList.add( mng ); fieldNameToCriteriaMngs.put( fieldName, mng ); alignments.put( mng, 0 ); } public void addCriteriaMng( ICriteriaMng mng, String fieldName ) { criteriaMngList.add( mng ); if( fieldName != null ) fieldNameToCriteriaMngs.put( fieldName, mng ); alignments.put( mng, 0 ); } public void addStandardPointingFieldCriteriaMngEx( String displayName, SearchMng searchMng, String pointingField, String pointedTable, String pointedField ) { ICriteriaMng mng = new StandardPointingFieldCriteriaMng( displayName, searchMng, pointingField, pointedTable, pointedField ); standardPointingFieldCriteriaMngs.put( displayName, mng ); criteriaMngList.add( mng ); fieldNameToCriteriaMngs.put( pointingField + "->" + pointedTable + "." + pointedField, mng ); alignments.put( mng, 1 ); } public void addStandardPointedByCriteriaMng( String displayName, SearchMng searchMng, String pointingTable ) { ICriteriaMng mng = new StandardPointedByCriteriaMng( displayName, searchMng, pointingTable ); standardPointedByCriteriaMngs.put( displayName, mng ); criteriaMngList.add( mng ); fieldNameToCriteriaMngs.put( "<-" + pointingTable, mng ); alignments.put( mng, 1 ); } @Override public SearchInstance createSearchWidget( boolean fReadOnly ) { return new SearchInstanceImpl( fReadOnly ); } public void init( String name ) { this.name = name; addStandardCombineOperator( "Or", "or" ); addStandardCombineOperator( "And", "and" ); } boolean getIsWidgetInlined( ICriteriaMng mng ) { Integer alignment = alignments.get( mng ); return alignment == 0; } void createSwitchAndAddChild( ICriteriaMng mng, INodeWidget<String, CriteriaSwitch> nodeWidget, JSONValue json, boolean fReadOnly ) { CriteriaSwitch cw = new CriteriaSwitch( criteriaMngList, new XCriteriaSwitch() { @Override public boolean getIsInline( ICriteriaMng mng ) { return getIsWidgetInlined( mng ); } }, null, fReadOnly ); cw.setCriteriaMng( mng ); if( json != null ) cw.setValue( json ); LeafWidget<String, CriteriaSwitch> leaf = new LeafWidget<String, CriteriaSwitch>( fReadOnly ); leaf.setWidget( cw ); leaf.setData( cw ); nodeWidget.addChild( leaf ); } private class SearchInstanceImpl implements SearchInstance, XNodeWidget<String, CriteriaSwitch> { private boolean fReadOnly; INodeWidgetFactory<String, CriteriaSwitch> nodeFactory = new INodeWidgetFactory<String, CriteriaSwitch>() { @Override public NodeWidget<String, CriteriaSwitch> create() { NodeWidget<String, CriteriaSwitch> w = new NodeWidget<String, CriteriaSwitch>( standardOperators, SearchInstanceImpl.this, this, fReadOnly ); if( !fReadOnly ) { for( ICriteriaMng mng : standardPointingFieldCriteriaMngs.values() ) addButton( w, mng ); for( ICriteriaMng mng : standardPointedByCriteriaMngs.values() ) addButton( w, mng ); } return w; } void addButton( final NodeWidget<String, CriteriaSwitch> nodeWidget, final ICriteriaMng mng ) { Anchor a = new Anchor( mng.getDisplayName() ); a.getElement().setClassName( Css.CSS.anchorButton() ); nodeWidget.addButton( a ); a.addClickHandler( new ClickHandler() { @Override public void onClick( ClickEvent event ) { event.preventDefault(); event.stopPropagation(); createSwitchAndAddChild( mng, nodeWidget, null, fReadOnly ); } } ); } }; NodeWidget<String, CriteriaSwitch> rootNode = nodeFactory.create(); public SearchInstanceImpl( boolean fReadOnly ) { this.fReadOnly = fReadOnly; } @Override public Widget asWidget() { return rootNode.asWidget(); } @Override public JSONValue getValue() { return getJSONForTree( rootNode ); } String getJSONString( JSONValue value ) { if( value == null ) return null; JSONString s = value.isString(); if( s == null ) return null; return s.stringValue(); } void pushValue( JSONValue json, NodeWidget<String, CriteriaSwitch> parentNode ) { if( json == null ) return; JSONObject obj = json.isObject(); if( obj == null ) return; String op = obj.get( "op" ).isString().stringValue(); if( op.equals( "->" ) ) { String field = getJSONString( obj.get( "field" ) ); String pointedTable = getJSONString( obj.get( "pointed_table" ) ); String pointedField = getJSONString( obj.get( "pointed_field" ) ); if( field == null || pointedTable == null || pointedField == null ) return; ICriteriaMng mng = fieldNameToCriteriaMngs.get( field + "->" + pointedTable + "." + pointedField ); createSwitchAndAddChild( mng, parentNode, obj.get( "sub" ), fReadOnly ); } else if( op.equals( "<-" ) ) { String pointingTable = obj.get( "pointing_table" ).isString().stringValue(); ICriteriaMng mng = fieldNameToCriteriaMngs.get( "<-" + pointingTable ); createSwitchAndAddChild( mng, parentNode, obj.get( "sub" ), fReadOnly ); } else if( op.equals( "or" ) || op.equals( "and" ) ) { NodeWidget<String, CriteriaSwitch> node = nodeFactory.create(); node.setData( op ); parentNode.addChild( node ); JSONArray ops = obj.get( "ops" ).isArray(); for( int i = 0; i < ops.size(); i++ ) { pushValue( ops.get( i ), node ); } } else { String field = obj.get( "field" ).isString().stringValue(); ICriteriaMng mng = fieldNameToCriteriaMngs.get( field ); createSwitchAndAddChild( mng, parentNode, obj, fReadOnly ); } } @Override public void setValue( JSONValue json ) { pushValue( json, rootNode ); } @Override public void onWantAdd( final INodeWidget<String, CriteriaSwitch> node, UIObject uiObject ) { // a node or sub node wants a child to be added to itself. // we are in charge of creating a new TreeWidget and // to give it to the node that asked for a creation final ListTable<ICriteriaMng> list = new ListTable<ICriteriaMng>(); for( ICriteriaMng mng : criteriaMngList ) list.addItem( mng.getDisplayName(), mng ); VerticalPanel p = new VerticalPanel(); p.add( new HTML( "<b>" + name + "</b> fields :" ) ); p.add( list ); final MyPopupPanel popup = new MyPopupPanel( true ); popup.setWidget( p ); popup.showRelativeTo( uiObject ); list.addChangeHandler( new ChangeHandler() { @Override public void onChange( ChangeEvent event ) { ICriteriaMng mng = list.getSelected(); createSwitchAndAddChild( mng, node, null, fReadOnly ); popup.hide(); } } ); } @Override public void onWantDelete( INodeWidget<String, CriteriaSwitch> node, TreeWidget<String, CriteriaSwitch> child, UIObject uiObject ) { node.removeChild( child ); } } private JSONValue getJSONForTree( TreeWidget<String, CriteriaSwitch> tree ) { INodeWidget<String, CriteriaSwitch> node = tree.isNode(); if( node != null ) return getJSONForNode( node ); ILeafWidget<String, CriteriaSwitch> leaf = tree.isLeaf(); if( leaf != null ) return getJSONForLeaf( leaf ); return null; } private JSONValue getJSONForLeaf( ILeafWidget<String, CriteriaSwitch> leaf ) { return leaf.getData().getValue(); } private JSONValue getJSONForNode( INodeWidget<String, CriteriaSwitch> node ) { ArrayList<TreeWidget<String, CriteriaSwitch>> children = node.getChildren(); if( children.isEmpty() ) return null; if( children.size() == 1 ) return getJSONForTree( children.get( 0 ) ); JSONArray array = new JSONArray(); for( int i = 0; i < children.size(); i++ ) array.set( i, getJSONForTree( children.get( i ) ) ); JSONObject obj = new JSONObject(); obj.put( "op", new JSONString( node.getData() ) ); obj.put( "ops", array ); return obj; } }