/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.rendering;
import java.util.Set;
import java.util.regex.Pattern;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import static org.novelang.parser.NodeKind.*;
import org.novelang.logger.Logger;
import org.novelang.logger.LoggerFactory;
import org.novelang.parser.NodeKind;
/**
* Handles tricky rules about inserting spaces at the right place.
*
* @author Laurent Caillette
*/
public class Spaces {
private static final Logger LOGGER = LoggerFactory.getLogger( Spaces.class );
private static final Set< Sequence > SEQUENCES = Sets.newHashSet() ;
static {
final Set< NodeKind > blocks = ImmutableSet.of(
BLOCK_AFTER_TILDE,
BLOCK_INSIDE_DOUBLE_QUOTES,
BLOCK_INSIDE_ASTERISK_PAIRS,
BLOCK_INSIDE_HYPHEN_PAIRS,
BLOCK_INSIDE_TWO_HYPHENS_THEN_HYPHEN_LOW_LINE,
BLOCK_INSIDE_PARENTHESIS,
BLOCK_INSIDE_SOLIDUS_PAIRS,
BLOCK_INSIDE_SQUARE_BRACKETS,
BLOCK_OF_LITERAL_INSIDE_GRAVE_ACCENTS,
BLOCK_OF_LITERAL_INSIDE_GRAVE_ACCENT_PAIRS
) ;
final Set< NodeKind > canonicalStuff = ImmutableSet.< NodeKind >builder().
addAll( blocks ).
add(
_URL,
WORD_
).build()
;
for( final NodeKind first : canonicalStuff ) {
for( final NodeKind second : canonicalStuff ) {
add( first, second ) ;
}
}
for( final NodeKind second : canonicalStuff ) {
add( PUNCTUATION_SIGN, second ) ;
}
add( PUNCTUATION_SIGN, APOSTROPHE_WORDMATE ) ;
for( final NodeKind first : blocks ) {
add( first, APOSTROPHE_WORDMATE ) ;
}
logSequences() ;
}
// =====================
// Literal normalization
// =====================
public static final char NO_BREAK_SPACE = '\u00a0' ;
private static final Pattern PATTERN = Pattern.compile( " +" );
private Spaces() {
}
public static String normalizeLiteral( final String rawLiteral ) {
String s = rawLiteral.trim();
s = PATTERN.matcher( s ).replaceAll( "" + NO_BREAK_SPACE );
return s ;
}
// =======
// Logging
// =======
private static void logSequences() {
int maximumNodeKindLength = 0 ;
for( final NodeKind nodeKind : NodeKind.values() ) {
maximumNodeKindLength = Math.max( maximumNodeKindLength, nodeKind.name().length() ) ;
}
final String format =
" " +
"%-" + maximumNodeKindLength + "s" +
" -> " +
"%-" + maximumNodeKindLength + "s" +
"\n"
;
final StringBuilder stringBuilder = new StringBuilder() ;
for( final Sequence sequence : SEQUENCES ) {
stringBuilder.append(
String.format( format, sequence.first.toString(), sequence.second.toString() ) ) ;
}
LOGGER.debug( "Added following sequences:\n", stringBuilder.toString() ) ;
}
// ============
// Boring stuff
// ============
private static void add( final NodeKind nodeKind1, final NodeKind nodeKind2 ) {
SEQUENCES.add( new Sequence( nodeKind1, nodeKind2 ) ) ;
}
public static boolean isTrigger( final NodeKind first, final NodeKind second ) {
if( null == first ) {
return false ;
}
final Sequence sequence = new Sequence( first, second ) ;
return SEQUENCES.contains( sequence ) ;
}
private static final class Sequence {
private final NodeKind first;
private final NodeKind second ;
public Sequence( final NodeKind first, final NodeKind second ) {
this.first = first;
this.second = second;
}
public NodeKind getFirst() {
return first;
}
public NodeKind getSecond() {
return second;
}
@Override
public boolean equals( final Object o ) {
if( this == o ) {
return true ;
}
if( o == null || getClass() != o.getClass() ) {
return false ;
}
final Sequence sequence = ( Sequence ) o ;
if( first != sequence.first ) {
return false;
}
if( second != sequence.second ) {
return false ;
}
return true ;
}
@Override
public int hashCode() {
int result = first != null ? first.hashCode() : 0 ;
result = 31 * result + ( second != null ? second.hashCode() : 0 ) ;
return result ;
}
}
}