/* * 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.aciitemeditor.sourceeditor; import org.apache.directory.studio.ldapbrowser.core.BrowserCoreConstants; import org.eclipse.jface.text.formatter.IFormattingStrategy; import org.eclipse.jface.text.source.ISourceViewer; /** * This class implements the formatting strategy for the ACI Item Editor. * <ul> * <li>New line after a comma * <li>New line after a opened left curly * </ul> * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class ACIFormattingStrategy implements IFormattingStrategy { /** The Constant INDENT_STRING. */ public static final String INDENT_STRING = " "; //$NON-NLS-1$ /** The Constant NEWLINE. */ public static final String NEWLINE = BrowserCoreConstants.LINE_SEPARATOR; /** The source viewer. */ private ISourceViewer sourceViewer; /** * Creates a new instance of ACIFormattingStrategy. * * @param sourceViewer the source viewer */ public ACIFormattingStrategy( ISourceViewer sourceViewer ) { this.sourceViewer = sourceViewer; } /** * {@inheritDoc} */ public String format( String content, boolean isLineStart, String indentation, int[] positions ) { String oldContent = sourceViewer.getDocument().get(); String newContent = internFormat( oldContent ); sourceViewer.getDocument().set( newContent ); return null; } /** * {@inheritDoc} */ public void formatterStarts( String initialIndentation ) { } /** * {@inheritDoc} */ public void formatterStops() { } private String internFormat( String content ) { StringBuilder sb = new StringBuilder(); // flag to track if a new line was started boolean newLineStarted = true; // flag to track if we are within a quoted string boolean inQuotedString = false; // flag to track if the current expression is appended in one-line mode boolean oneLineMode = false; // the current indent int indent = 0; int contentLength = content.length(); for ( int i = 0; i < contentLength; i++ ) { char c = content.charAt( i ); // track quotes if ( c == '"' ) { inQuotedString ^= true; } if ( ( c == '{' ) && !inQuotedString ) { // check one-line mode oneLineMode = checkInOneLine( i, content ); if ( oneLineMode ) { // no new line in one-line mode sb.append( c ); newLineStarted = false; } else { // start a new line, but avoid blank lines if there are multiple opened curlies if ( !newLineStarted ) { sb.append( NEWLINE ); for ( int x = 0; x < indent; x++ ) { sb.append( INDENT_STRING ); } } // append the curly sb.append( c ); // start a new line and increment indent sb.append( NEWLINE ); newLineStarted = true; indent++; for ( int x = 0; x < indent; x++ ) { sb.append( INDENT_STRING ); } } } else if ( ( c == '}' ) && !inQuotedString ) { if ( oneLineMode ) { // no new line in one-line mode sb.append( c ); newLineStarted = false; // closed curly indicates end of one-line mode oneLineMode = false; } else { // decrement indent indent--; // start a new line, but avoid blank lines if there are multiple closed curlies if ( newLineStarted ) { // delete one indent sb.delete( sb.length() - INDENT_STRING.length(), sb.length() ); } else { sb.append( NEWLINE ); for ( int x = 0; x < indent; x++ ) { sb.append( INDENT_STRING ); } } // append the curly sb.append( c ); // start a new line sb.append( NEWLINE ); newLineStarted = true; for ( int x = 0; x < indent; x++ ) { sb.append( INDENT_STRING ); } } } else if ( ( c == ',' ) && !inQuotedString ) { // start new line on comma if ( oneLineMode ) { sb.append( c ); newLineStarted = false; } else { sb.append( c ); sb.append( NEWLINE ); newLineStarted = true; for ( int x = 0; x < indent; x++ ) { sb.append( INDENT_STRING ); } } } else if ( Character.isWhitespace( c ) ) { char c1 = 'A'; if ( i + 1 < contentLength ) { c1 = content.charAt( i + 1 ); } if ( ( !newLineStarted ) && // ignore space after starting a new line ( c != '\n' ) && ( c != '\r' ) && // ignore new lines !Character.isWhitespace( c1 ) && ( c1 != '\n' ) && ( c1 != '\r' ) ) // compress whitespaces { sb.append( c ); } } else { // default case: append the char sb.append( c ); newLineStarted = false; } } return sb.toString(); } /** * Checks if an expression could be appended in one line. That is * if the expression starting at index i doesn't contain nested * expressions and only one comma. * * @param i the starting index of the expression * @param content the content * * @return true, if the expression could be appended in one line */ private boolean checkInOneLine( int i, String content ) { // flag to track if we are within a quoted string boolean inQuote = false; // counter for commas int commaCounter = 0; int contentLength = content.length(); for ( int k = i + 1; k < contentLength; k++ ) { char c = content.charAt( k ); // track quotes if ( c == '"' ) { inQuote ^= true; } // open curly indicates nested expression if ( ( c == '{' ) && !inQuote ) { return false; } // closing curly indicates end of expression if ( c == '}' && !inQuote ) { return true; } // allow only single comma in an expression in one line if ( c == ',' && !inQuote ) { commaCounter++; if ( commaCounter > 1 ) { return false; } } } return false; } }