/* * This file is part of the OpenSCADA project * Copyright (C) 2006-2012 TH4 SYSTEMS GmbH (http://th4-systems.com) * * OpenSCADA is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenSCADA 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 Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenSCADA. If not, see * <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License. */ package org.openscada.ae.ui.views.dialog; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.DateTime; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.statushandlers.StatusManager; import org.openscada.ae.Event; import org.openscada.ae.ui.views.Activator; import org.openscada.ae.ui.views.Messages; import org.openscada.core.Variant; import org.openscada.core.VariantEditor; import org.openscada.ui.utils.status.StatusHelper; import org.openscada.utils.filter.Assertion; import org.openscada.utils.filter.Filter; import org.openscada.utils.filter.FilterAssertion; import org.openscada.utils.filter.FilterExpression; import org.openscada.utils.filter.FilterParser; import org.openscada.utils.filter.Operator; import org.openscada.utils.lang.Pair; import org.openscada.utils.propertyeditors.DateEditor; import org.openscada.utils.str.StringHelper; public class FilterQueryByExampleComposite extends Composite { private interface FilterModified { public void onModified (); } private interface FieldEntry { public abstract Filter asExpression (); public abstract boolean isEmpty (); public abstract void clear (); public abstract void focus (); } private static class DateFieldEntry implements FieldEntry { private final String field; private final Label captionLabel; private final Label fromLabel; private final Label toLabel; private final DateTime fromDate; private final DateTime fromTime; private final DateTime toDate; private final DateTime toTime; private final Button useCheckbox; private final FilterModified filterModified; private static final DateFormat isoDateFormat = new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss.SSS" ); //$NON-NLS-1$ public DateFieldEntry ( final Composite parent, final String field, final String caption, final FilterModified filterModified ) { final GridData dateLayoutData = new GridData (); dateLayoutData.widthHint = 180; this.field = field; this.captionLabel = new Label ( parent, SWT.NONE ); this.captionLabel.setText ( caption ); this.filterModified = filterModified; this.useCheckbox = new Button ( parent, SWT.CHECK ); this.useCheckbox.addSelectionListener ( new SelectionAdapter () { @Override public void widgetSelected ( final SelectionEvent e ) { updateFromCheckbox (); filterModified.onModified (); } } ); this.fromLabel = new Label ( parent, SWT.NONE ); this.fromLabel.setText ( Messages.from ); final SelectionAdapter updater = new SelectionAdapter () { @Override public void widgetSelected ( final SelectionEvent e ) { filterModified.onModified (); } }; Composite wrapper; wrapper = new Composite ( parent, SWT.NONE ); wrapper.setLayout ( new GridLayout ( 2, false ) ); this.fromDate = new DateTime ( wrapper, SWT.DATE | needBorder () ); this.fromTime = new DateTime ( wrapper, SWT.TIME | needBorder () ); this.fromDate.addSelectionListener ( updater ); this.fromTime.addSelectionListener ( updater ); this.toLabel = new Label ( parent, SWT.NONE ); this.toLabel.setText ( Messages.to ); wrapper = new Composite ( parent, SWT.NONE ); wrapper.setLayout ( new GridLayout ( 2, false ) ); this.toDate = new DateTime ( wrapper, SWT.DATE | needBorder () ); this.toTime = new DateTime ( wrapper, SWT.TIME | needBorder () ); this.toDate.addSelectionListener ( updater ); this.toTime.addSelectionListener ( updater ); updateFromCheckbox (); } protected void updateFromCheckbox () { final boolean enabled = this.useCheckbox.getSelection (); this.fromDate.setEnabled ( enabled ); this.fromTime.setEnabled ( enabled ); this.toDate.setEnabled ( enabled ); this.toTime.setEnabled ( enabled ); } @Override public Filter asExpression () { FilterAssertion assertionFrom = null; FilterAssertion assertionTo = null; final FilterExpression expression = new FilterExpression (); expression.setOperator ( Operator.AND ); if ( this.useCheckbox.getSelection () ) { // from final Calendar from = new GregorianCalendar (); from.set ( this.fromDate.getYear (), this.fromDate.getMonth (), this.fromDate.getDay (), this.fromTime.getHours (), this.fromTime.getMinutes (), this.fromTime.getSeconds () ); assertionFrom = new FilterAssertion ( this.field, Assertion.GREATEREQ, isoDateFormat.format ( from.getTime () ) ); expression.getFilterSet ().add ( assertionFrom ); // to final Calendar to = new GregorianCalendar (); to.set ( this.toDate.getYear (), this.toDate.getMonth (), this.toDate.getDay (), this.toTime.getHours (), this.toTime.getMinutes (), this.toTime.getSeconds () ); assertionTo = new FilterAssertion ( this.field, Assertion.LESSEQ, isoDateFormat.format ( to.getTime () ) ); expression.getFilterSet ().add ( assertionTo ); } return expression; } @Override public boolean isEmpty () { return !this.useCheckbox.getSelection (); } @Override public void clear () { this.useCheckbox.setSelection ( false ); updateFromCheckbox (); this.filterModified.onModified (); } @Override public void focus () { this.fromDate.setFocus (); } public void setFrom ( final Date date ) { this.useCheckbox.setSelection ( true ); updateFromCheckbox (); final Calendar c = new GregorianCalendar (); c.setTime ( date ); this.fromDate.setDate ( c.get ( Calendar.YEAR ), c.get ( Calendar.MONTH ), c.get ( Calendar.DAY_OF_MONTH ) ); this.fromTime.setTime ( c.get ( Calendar.HOUR_OF_DAY ), c.get ( Calendar.MINUTE ), c.get ( Calendar.SECOND ) ); } public void setTo ( final Date date ) { this.useCheckbox.setSelection ( true ); updateFromCheckbox (); final Calendar c = new GregorianCalendar (); c.setTime ( date ); this.toDate.setDate ( c.get ( Calendar.YEAR ), c.get ( Calendar.MONTH ), c.get ( Calendar.DAY_OF_MONTH ) ); this.toTime.setTime ( c.get ( Calendar.HOUR_OF_DAY ), c.get ( Calendar.MINUTE ), c.get ( Calendar.SECOND ) ); } } private static class TextFieldEntry implements FieldEntry { protected final String field; protected final Label captionLabel; protected final Button notCheckBox; protected final Text textText; public TextFieldEntry ( final Composite parent, final String field, final String caption, final FilterModified filterModified ) { this.field = field; this.captionLabel = new Label ( parent, SWT.NONE ); this.captionLabel.setText ( caption ); this.notCheckBox = new Button ( parent, SWT.CHECK ); this.notCheckBox.setText ( Messages.not ); this.notCheckBox.addSelectionListener ( new SelectionListener () { @Override public void widgetSelected ( final SelectionEvent e ) { filterModified.onModified (); } @Override public void widgetDefaultSelected ( final SelectionEvent e ) { } } ); this.textText = new Text ( parent, SWT.BORDER ); final GridData layoutData = new GridData (); layoutData.horizontalAlignment = GridData.FILL; layoutData.horizontalSpan = 4; layoutData.grabExcessHorizontalSpace = true; this.textText.setLayoutData ( layoutData ); this.textText.addSelectionListener ( new SelectionAdapter () { @Override public void widgetSelected ( final SelectionEvent e ) { filterModified.onModified (); } } ); this.textText.addModifyListener ( new ModifyListener () { @Override public void modifyText ( final ModifyEvent e ) { filterModified.onModified (); } } ); } @Override public Filter asExpression () { final FilterAssertion assertion; if ( this.textText.getText ().contains ( "*" ) ) //$NON-NLS-1$ { assertion = new FilterAssertion ( this.field, Assertion.SUBSTRING, toCollection ( this.textText.getText () ) ); } else { assertion = new FilterAssertion ( this.field, Assertion.EQUALITY, this.textText.getText () ); } if ( this.notCheckBox.getSelection () ) { return FilterExpression.negate ( assertion ); } return assertion; } @Override public boolean isEmpty () { return this.textText.getText ().trim ().length () == 0; } @Override public void clear () { this.textText.setText ( "" );//$NON-NLS-1$ } @Override public void focus () { this.textText.setFocus (); } public void setValue ( final String value ) { this.textText.setText ( value ); } public void setNegation ( final boolean negate ) { this.notCheckBox.setSelection ( negate ); } } // clear button final Button clearButton; private final Map<String, FieldEntry> fields = new HashMap<String, FieldEntry> (); public FilterQueryByExampleComposite ( final FilterChangedListener filterChangedListener, final Composite parent, final int style, final String filter ) { super ( parent, style ); final GridLayout layout = new GridLayout (); layout.numColumns = 6; layout.marginHeight = 12; layout.marginWidth = 12; setLayout ( layout ); final FilterModified filterModified = new FilterModified () { @Override public void onModified () { final String filterString = toFilter ().toString (); filterChangedListener.onFilterChanged ( new Pair<SearchType, String> ( SearchType.SIMPLE, filterString ) ); }; }; this.fields.put ( "sourceTimestamp", new DateFieldEntry ( this, "sourceTimestamp", Messages.getString ( "sourceTimestamp" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "entryTimestamp", new DateFieldEntry ( this, "entryTimestamp", Messages.getString ( "entryTimestamp" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "message", new TextFieldEntry ( this, "message", Messages.getString ( "message" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "monitorType", new TextFieldEntry ( this, "monitorType", Messages.getString ( "monitorType" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "eventType", new TextFieldEntry ( this, "eventType", Messages.getString ( "eventType" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "item", new TextFieldEntry ( this, "item", Messages.getString ( "item" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "value", new TextFieldEntry ( this, "value", Messages.getString ( "value" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "priority", new TextFieldEntry ( this, "priority", Messages.getString ( "priority" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "source", new TextFieldEntry ( this, "source", Messages.getString ( "source" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "actorType", new TextFieldEntry ( this, "actorType", Messages.getString ( "actorType" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "actorName", new TextFieldEntry ( this, "actorName", Messages.getString ( "actorName" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "component", new TextFieldEntry ( this, "component", Messages.getString ( "component" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "system", new TextFieldEntry ( this, "system", Messages.getString ( "system" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "hive", new TextFieldEntry ( this, "hive", Messages.getString ( "hive" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( "location", new TextFieldEntry ( this, "location", Messages.getString ( "location" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.fields.put ( Event.Fields.MESSAGE_CODE.getName (), new TextFieldEntry ( this, Event.Fields.MESSAGE_CODE.getName (), Messages.getString ( Event.Fields.MESSAGE_CODE.getName () ), filterModified ) ); this.fields.put ( "comment", new TextFieldEntry ( this, "comment", Messages.getString ( "comment" ), filterModified ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // clear button this.clearButton = new Button ( this, SWT.PUSH ); this.clearButton.setText ( Messages.clear ); final GridData clearButtonLayoutData = new GridData (); clearButtonLayoutData.horizontalAlignment = SWT.BEGINNING; clearButtonLayoutData.horizontalSpan = 6; this.clearButton.setLayoutData ( clearButtonLayoutData ); this.clearButton.addSelectionListener ( new SelectionAdapter () { @Override public void widgetSelected ( final SelectionEvent e ) { for ( final FieldEntry fieldEntry : FilterQueryByExampleComposite.this.fields.values () ) { fieldEntry.clear (); } FilterQueryByExampleComposite.this.fields.get ( "sourceTimestamp" ).focus (); //$NON-NLS-1$ } } ); populateFromFilter ( filter ); this.fields.get ( "sourceTimestamp" ).focus (); //$NON-NLS-1$ } public static int needBorder () { return SWT.BORDER; } private void populateFromFilter ( final String filterString ) { // no filter given if ( filterString == null || filterString.length () == 0 ) { return; } // it has to be an expression final Filter filter; try { filter = new FilterParser ( filterString ).getFilter (); } catch ( final Exception e ) { StatusManager.getManager ().handle ( StatusHelper.convertStatus ( Activator.PLUGIN_ID, e ), StatusManager.LOG ); return; } if ( !filter.isExpression () ) { return; } final FilterExpression filterExpression = (FilterExpression)filter; // and it has to be a and conjunction if ( filterExpression.getOperator () != Operator.AND ) { return; } for ( final Filter subFilter : filterExpression.getFilterSet () ) { if ( subFilter.isAssertion () ) { // normal case populateFromAssertion ( false, (FilterAssertion)subFilter ); } else if ( subFilter.isExpression () ) { // negation final FilterExpression subFilterExpression = (FilterExpression)subFilter; if ( subFilterExpression.getOperator () == Operator.NOT ) { if ( subFilterExpression.getFilterSet ().size () == 1 && subFilterExpression.getFilterSet ().get ( 0 ).isAssertion () ) { populateFromAssertion ( true, (FilterAssertion)subFilterExpression.getFilterSet ().get ( 0 ) ); } } // special handling for date else if ( subFilterExpression.getOperator () == Operator.AND ) { String attribute = null; String from = null; String to = null; if ( subFilterExpression.getFilterSet ().size () == 1 || subFilterExpression.getFilterSet ().size () == 2 && subFilterExpression.getFilterSet ().get ( 0 ).isAssertion () ) { final FilterAssertion filterAssertion = (FilterAssertion)subFilterExpression.getFilterSet ().get ( 0 ); attribute = filterAssertion.getAttribute (); if ( filterAssertion != null && filterAssertion.getAssertion () == Assertion.GREATEREQ ) { from = (String)filterAssertion.getValue (); } else if ( filterAssertion != null && filterAssertion.getAssertion () == Assertion.LESSEQ ) { to = (String)filterAssertion.getValue (); } } if ( subFilterExpression.getFilterSet ().size () == 2 && subFilterExpression.getFilterSet ().get ( 1 ).isAssertion () ) { final FilterAssertion filterAssertion = (FilterAssertion)subFilterExpression.getFilterSet ().get ( 1 ); if ( filterAssertion != null && filterAssertion.getAssertion () == Assertion.GREATEREQ ) { from = (String)filterAssertion.getValue (); } else if ( filterAssertion != null && filterAssertion.getAssertion () == Assertion.LESSEQ ) { to = (String)filterAssertion.getValue (); } } populateFromDate ( attribute, from, to ); } } } } private void populateFromAssertion ( final boolean negate, final FilterAssertion filter ) { final FieldEntry fieldEntry = this.fields.get ( filter.getAttribute () ); if ( fieldEntry instanceof TextFieldEntry ) { final VariantEditor ve = new VariantEditor (); // special case if filter is substring if ( filter.getValue () instanceof String ) { ve.setAsText ( (String)filter.getValue () ); } else if ( filter.getValue () instanceof Collection<?> ) { ve.setAsText ( StringHelper.join ( (Collection<?>)filter.getValue (), "*" ) ); //$NON-NLS-1$ } final Variant value = (Variant)ve.getValue (); ( (TextFieldEntry)fieldEntry ).setValue ( value.toLabel ( "" ) ); //$NON-NLS-1$ ( (TextFieldEntry)fieldEntry ).setNegation ( negate ); } } private void populateFromDate ( final String fieldName, final String from, final String to ) { final FieldEntry fieldEntry = this.fields.get ( fieldName ); if ( fieldEntry instanceof DateFieldEntry ) { final DateEditor dateEditor = new DateEditor (); final DateFieldEntry dateFieldEntry = (DateFieldEntry)fieldEntry; dateEditor.setAsText ( from ); dateFieldEntry.setFrom ( (Date)dateEditor.getValue () ); dateEditor.setAsText ( to ); dateFieldEntry.setTo ( (Date)dateEditor.getValue () ); } } private Filter toFilter () { final FilterExpression filter = new FilterExpression (); filter.setOperator ( Operator.AND ); for ( final FieldEntry fieldEntry : this.fields.values () ) { if ( !fieldEntry.isEmpty () ) { filter.getFilterSet ().add ( fieldEntry.asExpression () ); } } return filter; } private static Collection<String> toCollection ( final String text ) { final ArrayList<String> result = new ArrayList<String> (); result.addAll ( Arrays.asList ( text.split ( "\\*" ) ) ); //$NON-NLS-1$ if ( text.endsWith ( "*" ) ) //$NON-NLS-1$ { result.add ( "" ); //$NON-NLS-1$ } return result; } }