/**
* Licensed to Neo Technology under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Neo Technology licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.neo4j.neoclipse.search;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.neoclipse.Activator;
import org.neo4j.neoclipse.graphdb.GraphCallable;
import org.neo4j.neoclipse.graphdb.GraphDbServiceEvent;
import org.neo4j.neoclipse.graphdb.GraphDbServiceEventListener;
import org.neo4j.neoclipse.graphdb.GraphDbServiceManager;
/**
* This class represents a search page in the search dialog to perform
* Neo4j-specific searches. The found nodes will be shown in the search result
* view.
*
* @author Peter Hänsgen
* @author Anders Nawroth
*/
public class NeoSearchPage extends DialogPage implements ISearchPage
{
private static final int QUERY_MODE = 1;
private static final int EXACT_MODE = 0;
public static final String ID = "org.neo4j.neoclipse.search.NeoSearchPage";
/**
* Choose the index to search.
*/
private Tree indexTree;
/**
* The input field for the search expression.
*/
private Text valueOrQueryField;
/**
* The container of this page.
*/
protected ISearchPageContainer container;
private Text keyField;
private TreeItem nodeRoot;
private TreeItem relRoot;
private Combo modeCombo;
private GraphDbServiceEventListener listener;
private GraphDbServiceManager gsm;
/**
* Initializes the content of the search page.
*/
@Override
public void createControl( final Composite parent )
{
initializeDialogUnits( parent );
final Composite comp = new Composite( parent, SWT.NONE );
GridLayout layout = new GridLayout( 1, false );
comp.setLayout( layout );
Label indexLabel = new Label( comp, SWT.NONE );
indexLabel.setText( "Index to search:" );
indexLabel.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
indexTree = new Tree( comp, SWT.BORDER | SWT.CHECK );
GridData treeLayout = new GridData( GridData.FILL_BOTH );
treeLayout.heightHint = 300;
indexTree.setLayoutData( treeLayout );
indexTree.addListener( SWT.Selection, new Listener()
{
@Override
public void handleEvent( final Event event )
{
if ( event.detail == SWT.CHECK )
{
TreeItem item = (TreeItem) event.item;
boolean checked = item.getChecked();
checkItems( item, checked );
checkPath( item.getParentItem(), checked, false );
}
}
} );
Label modeLabel = new Label( comp, SWT.NONE );
modeLabel.setText( "Search mode:" );
modeLabel.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
modeCombo = new Combo( comp, SWT.READ_ONLY );
modeCombo.add( "Exact matches", EXACT_MODE );
modeCombo.add( "Query", QUERY_MODE );
modeCombo.select( EXACT_MODE );
Label propertyLabel = new Label( comp, SWT.NONE );
propertyLabel.setText( "Key: (leave empty for Compound Queries)" );
propertyLabel.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
keyField = new Text( comp, SWT.SINGLE | SWT.BORDER );
keyField.setLayoutData( new GridData( GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_END ) );
Label expressionLabel = new Label( comp, SWT.NONE );
expressionLabel.setText( "Value/Query:" );
expressionLabel.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
valueOrQueryField = new Text( comp, SWT.SINGLE | SWT.BORDER );
valueOrQueryField.setLayoutData( new GridData(
GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_END ) );
gsm = Activator.getDefault().getGraphDbServiceManager();
listener = new GraphDbServiceEventListener()
{
@Override
public void serviceChanged( final GraphDbServiceEvent event )
{
switch ( event.getStatus() )
{
case STARTED:
loadIndices();
break;
case STOPPED:
clearIndices();
break;
case SHUTTING_DOWN:
break;
}
}
};
gsm.addServiceEventListener( listener );
comp.getShell().addListener( SWT.Show, new Listener()
{
@Override
public void handleEvent( final Event event )
{
if ( nodeRoot == null )
{
loadIndices();
}
}
} );
setControl( comp );
}
/**
* Sets the owning search dialog.
*/
@Override
public void setContainer( final ISearchPageContainer container )
{
this.container = container;
}
/**
* Performs the search.
*/
@Override
public boolean performAction()
{
String searchString = valueOrQueryField.getText();
String propertyName = keyField.getText();
if ( nodeRoot != null )
{
IndexSearch search;
if ( modeCombo.getSelectionIndex() == QUERY_MODE )
{
search = IndexSearch.query( propertyName, searchString,
namesFromRoot( nodeRoot ), namesFromRoot( relRoot ) );
}
else
{
search = IndexSearch.exact( propertyName, searchString,
namesFromRoot( nodeRoot ), namesFromRoot( relRoot ) );
}
NewSearchUI.runQueryInBackground( new NeoSearchQuery( search ) );
return true;
}
return false;
}
private Iterable<String> namesFromRoot( final TreeItem treeItem )
{
if ( treeItem == null )
{
return Collections.emptyList();
}
List<String> names = new ArrayList<String>();
for ( TreeItem item : treeItem.getItems() )
{
if ( item.getChecked() )
{
names.add( item.getText() );
}
}
return names;
}
private void clearIndices()
{
if ( getControl().isDisposed() || indexTree.isDisposed() )
{
return;
}
indexTree.removeAll();
}
private void loadIndices()
{
// load indices
if ( !gsm.isRunning() )
{
return;
}
final String[] nodeIndexNames = gsm.executeTask(
new GraphCallable<String[]>()
{
@Override
public String[] call( final GraphDatabaseService graphDb )
{
return graphDb.index().nodeIndexNames();
}
}, "Get node index names." );
Arrays.sort( nodeIndexNames );
final String[] relIndexNames = gsm.executeTask(
new GraphCallable<String[]>()
{
@Override
public String[] call( final GraphDatabaseService graphDb )
{
return graphDb.index().relationshipIndexNames();
}
}, "Get relationship index names." );
Arrays.sort( relIndexNames );
gsm.submitDisplayTask( new Runnable()
{
@Override
public void run()
{
if ( getControl().isDisposed() || !getControl().isVisible()
|| indexTree.isDisposed() )
{
return;
}
indexTree.removeAll();
if ( nodeIndexNames.length > 0 )
{
nodeRoot = new TreeItem( indexTree, SWT.None );
nodeRoot.setText( "Nodes" );
for ( String name : nodeIndexNames )
{
TreeItem item = new TreeItem( nodeRoot, SWT.None );
item.setText( name );
}
}
if ( relIndexNames.length > 0 )
{
relRoot = new TreeItem( indexTree, SWT.None );
relRoot.setText( "Relationships" );
for ( String name : relIndexNames )
{
TreeItem item = new TreeItem( relRoot, SWT.None );
item.setText( name );
}
}
}
}, "Add index names to the UI." );
}
private static void checkPath( final TreeItem item, boolean checked,
boolean grayed )
{
if ( item == null )
{
return;
}
if ( grayed )
{
checked = true;
}
else
{
int index = 0;
TreeItem[] items = item.getItems();
while ( index < items.length )
{
TreeItem child = items[index];
if ( child.getGrayed() || checked != child.getChecked() )
{
checked = grayed = true;
break;
}
index++;
}
}
item.setChecked( checked );
item.setGrayed( grayed );
checkPath( item.getParentItem(), checked, grayed );
}
private static void checkItems( final TreeItem item, final boolean checked )
{
item.setGrayed( false );
item.setChecked( checked );
TreeItem[] items = item.getItems();
for ( TreeItem item2 : items )
{
checkItems( item2, checked );
}
}
}