/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.directory.studio.ldapbrowser.common.filtereditor; import org.apache.directory.studio.ldapbrowser.core.model.filter.LdapAndFilterComponent; import org.apache.directory.studio.ldapbrowser.core.model.filter.LdapFilter; import org.apache.directory.studio.ldapbrowser.core.model.filter.LdapFilterComponent; import org.apache.directory.studio.ldapbrowser.core.model.filter.LdapNotFilterComponent; import org.apache.directory.studio.ldapbrowser.core.model.filter.LdapOrFilterComponent; import org.apache.directory.studio.ldapbrowser.core.model.filter.parser.LdapFilterParser; import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; /** * The FilterAutoEditStrategy implements the IAutoEditStrategy for the filter editor widget. * It provides smart parentesis handling when typing the filter. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class FilterAutoEditStrategy extends DefaultIndentLineAutoEditStrategy implements IAutoEditStrategy { /** The Constant INDENT_STRING. */ public static final String INDENT_STRING = " "; //$NON-NLS-1$ /** The filter parser. */ private LdapFilterParser parser; /** * Creates a new instance of FilterAutoEditStrategy. * * @param parser the filter parser */ public FilterAutoEditStrategy( LdapFilterParser parser ) { this.parser = parser; } /** * @see org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand) */ public void customizeDocumentCommand( IDocument d, DocumentCommand c ) { super.customizeDocumentCommand( d, c ); AutoEditParameters aep = new AutoEditParameters( c.text, c.offset, c.length, c.caretOffset, c.shiftsCaret ); customizeAutoEditParameters( d.get(), aep ); c.offset = aep.offset; c.length = aep.length; c.text = aep.text; c.caretOffset = aep.caretOffset; c.shiftsCaret = aep.shiftsCaret; } /** * Customizes auto edit parameters. * * @param currentFilter the current filter * @param aep the auto edit parameters */ public void customizeAutoEditParameters( String currentFilter, AutoEditParameters aep ) { parser.parse( currentFilter ); LdapFilter filter = parser.getModel().getFilter( aep.offset ); if ( filter == null ) { return; } // check balanced parenthesis int balanced = 0; for ( int i = 0; i < currentFilter.length(); i++ ) { if ( currentFilter.charAt( i ) == '(' ) { balanced++; } else if ( currentFilter.charAt( i ) == ')' ) { balanced--; } } if ( aep.length > 0 && ( aep.text == null || "".equals( aep.text ) ) ) //$NON-NLS-1$ { // delete surrounding parenthesis after deleting the last character if ( filter.toString().length() - aep.length == 2 && filter.getStartToken() != null && filter.getStopToken() != null && aep.offset >= filter.getStartToken().getOffset() + filter.getStartToken().getLength() && aep.offset + aep.length <= filter.getStopToken().getOffset() ) { if ( filter.toString().length() - aep.length == 2 ) { aep.offset -= 1; aep.length += 2; aep.caretOffset = aep.offset; aep.shiftsCaret = false; } } // delete closing parenthesis after deleting the opening parenthesis if ( filter.toString().length() - aep.length == 1 && filter.getStartToken() != null && filter.getStopToken() != null && aep.offset == filter.getStartToken().getOffset() ) { aep.length += 1; aep.caretOffset = aep.offset; aep.shiftsCaret = false; } } if ( ( aep.length == 0 || aep.length == currentFilter.length() ) && aep.text != null && !"".equals( aep.text ) ) //$NON-NLS-1$ { boolean isNewFilter = aep.text.equals( "(" ); //$NON-NLS-1$ boolean isNewNestedFilter = aep.text.equals( "&" ) || aep.text.equals( "|" ) || aep.text.equals( "!" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ boolean isSurroundNew = false; boolean isSurroundNested = false; boolean isSurroundBeforeOtherFilter = false; boolean isSurroundAfterOtherFilter = false; if ( !Character.isWhitespace( aep.text.charAt( 0 ) ) && !aep.text.startsWith( "(" ) && !aep.text.endsWith( ")" ) ) //$NON-NLS-1$ //$NON-NLS-2$ { // isSurroundNew isSurroundNew = aep.offset == 0; // isSurroundNested if ( filter.getStartToken() != null && ( filter.getFilterComponent() instanceof LdapAndFilterComponent || filter.getFilterComponent() instanceof LdapOrFilterComponent || filter.getFilterComponent() instanceof LdapNotFilterComponent ) ) { LdapFilterComponent fc = filter.getFilterComponent(); LdapFilter[] filters = fc.getFilters(); if ( filters.length == 0 && aep.offset > fc.getStartToken().getOffset() ) { // no nested filter yet isSurroundNested = true; } if ( filters.length > 0 && aep.offset > fc.getStartToken().getOffset() && aep.offset < filters[0].getStartToken().getOffset() ) { // before first nested filter isSurroundNested = true; } if ( filters.length > 0 && aep.offset > filters[filters.length - 1].getStopToken().getOffset() && aep.offset <= filter.getStopToken().getOffset() ) { // after last nested filter isSurroundNested = true; } for ( int i = 0; i < filters.length; i++ ) { if ( filters.length > i + 1 ) { if ( aep.offset > filters[i].getStopToken().getOffset() && aep.offset <= filters[i + 1].getStopToken().getOffset() ) { // between nested filter isSurroundNested = true; } } } } // isSurroundBeforeOtherFilter isSurroundBeforeOtherFilter = filter.getStartToken() != null && aep.offset == filter.getStartToken().getOffset(); // isSurroundAfterOtherFilter isSurroundAfterOtherFilter = filter.getStopToken() != null && aep.offset == filter.getStopToken().getOffset() && ( filter.getFilterComponent() instanceof LdapAndFilterComponent || filter.getFilterComponent() instanceof LdapOrFilterComponent || filter.getFilterComponent() instanceof LdapNotFilterComponent ); } // add opening parenthesis '(' if ( isSurroundNew || isSurroundNested || isSurroundAfterOtherFilter || isSurroundBeforeOtherFilter ) { aep.text = "(" + aep.text; //$NON-NLS-1$ aep.caretOffset = aep.offset + aep.text.length(); aep.shiftsCaret = false; } // add parenthesis for nested filters if ( isNewNestedFilter ) { aep.text = aep.text + "()"; //$NON-NLS-1$ aep.caretOffset = aep.offset + aep.text.length() - 1; aep.shiftsCaret = false; } // add closing parenthesis ')' if ( isNewFilter || isSurroundNew || isSurroundNested || isSurroundAfterOtherFilter || isSurroundBeforeOtherFilter ) { if ( balanced == 0 ) { aep.text = aep.text + ")"; //$NON-NLS-1$ if ( aep.caretOffset == -1 ) { aep.caretOffset = aep.offset + aep.text.length() - 1; aep.shiftsCaret = false; } } } // translate tab to IDENT_STRING if ( aep.text.equals( "\t" ) ) //$NON-NLS-1$ { aep.text = INDENT_STRING; } } } /** * Helper class. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public static class AutoEditParameters { /** The text. */ public String text; /** The offset. */ public int offset; /** The length. */ public int length; /** The caret offset. */ public int caretOffset; /** The shifts caret flag. */ public boolean shiftsCaret; /** * Creates a new instance of AutoEditParameters. * * @param text the text * @param offset the offset * @param length the length * @param caretOffset the caret offset * @param shiftsCaret the shifts caret flag */ public AutoEditParameters( String text, int offset, int length, int caretOffset, boolean shiftsCaret ) { this.text = text; this.offset = offset; this.length = length; this.caretOffset = caretOffset; this.shiftsCaret = shiftsCaret; } } }