org.opencypher.generator.Node Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2015-2018 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* Licensed 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.
*
* Attribution Notice under the terms of the Apache License 2.0
*
* This work was created by the collective efforts of the openCypher community.
* Without limiting the terms of Section 6, any Derivative Work that is not
* approved by the public consensus process of the openCypher Implementers Group
* should not be described as “Cypher” (and Cypher® is a registered trademark of
* Neo4j Inc.) or as "openCypher". Extensions by implementers or prototypes or
* proposals for change that have been documented or implemented should only be
* described as "implementation extensions to Cypher" or as "proposed changes to
* Cypher that are not yet approved by the openCypher community".
*/
package org.opencypher.generator;
import java.util.Objects;
import java.util.function.Consumer;
import org.opencypher.tools.io.Output;
import static java.util.Objects.requireNonNull;
import static org.opencypher.tools.io.Output.stringBuilder;
public abstract class Node
{
static Tree root( String language )
{
return new Tree( null, requireNonNull( language, "language" ) );
}
private final Node parent;
private final String name;
private Node next;
private Node( Node parent, String name )
{
this.parent = parent;
this.name = name;
}
public final Node parent()
{
return parent;
}
public void write( Output output )
{
for ( Node child = children(); child != null; child = child.next )
{
child.write( output );
}
}
@Override
public final int hashCode()
{
int hash = Objects.hashCode( name );
int detail = hash();
if ( detail != 0 )
{
hash = hash * 31 + detail;
}
for ( Node child = children(); child != null; child = child.next )
{
hash = hash * 31 + child.hashCode();
}
return hash;
}
int hash()
{
return 0;
}
@Override
public final boolean equals( Object obj )
{
if ( this == obj )
{
return true;
}
if ( obj == null || getClass() != obj.getClass() || !eq( (Node) obj ) )
{
return false;
}
Node these = children(), those = ((Node) obj).children();
for (; these != null && those != null; these = these.next, those = those.next )
{
if ( !these.equals( those ) )
{
return false;
}
}
return these == those;
}
boolean eq( Node obj )
{
return true;
}
@Override
public final String toString()
{
Output result = stringBuilder();
toString( result );
return result.toString();
}
public final void sExpression( Output output )
{
sExpression( output, 0 );
}
void toString( Output result )
{
result.append( getClass().getSimpleName() ).append( '{' ).append( name() );
String sep = ": ";
for ( Node child = children(); child != null; child = child.next )
{
result.append( sep );
child.toString( result );
sep = ", ";
}
result.append( '}' );
}
void sExpression( Output output, int indent )
{
indent += 2;
output.append( '(' ).append( name() );
Node child = children();
if ( child != null && child.next == null )
{
output.append( ' ' );
child.sExpression( output, indent );
}
else
{
for (; child != null; child = child.next )
{
output.println();
for ( int space = 0; space < indent; space++ )
{
output.append( ' ' );
}
child.sExpression( output, indent );
}
}
output.append( ')' );
}
Node append( String literal )
{
return append( new Literal( parent() ) ).append( literal );
}
Node appendCodePoint( int cp )
{
return append( new Literal( parent() ) ).appendCodePoint( cp );
}
Node children()
{
return null;
}
public final String name()
{
return name;
}
final Node append( Node node )
{
assert next == null : "appending to the middle of the chain";
next = node;
return node;
}
static class Tree extends Node
{
private Node first, last;
private Tree( Node parent, String name )
{
super( parent, name );
}
@Override
Node children()
{
return first;
}
public Tree child( String name )
{
return add( new Tree( this, requireNonNull( name, "name" ) ) );
}
public void literal( CharSequence literal )
{
if ( last == null )
{
first = last = new Literal( this );
}
last = last.append( literal.toString() );
}
public void codePoint( int cp )
{
if ( last == null )
{
first = last = new Literal( this );
}
last = last.appendCodePoint( cp );
}
public void production(
String name, ProductionReplacement replacement, T context, Consumer defaultValue )
{
add( new Replacement<>( this, name, replacement, context, defaultValue ) );
}
private T add( T child )
{
if ( last == null )
{
first = last = child;
}
else
{
last = last.append( child );
}
return child;
}
}
private static class Literal extends Node
{
private final StringBuilder buffer = new StringBuilder();
private Literal( Node parent )
{
super( parent, null );
}
@Override
int hash()
{
int hash = 0;
for ( int i = buffer.length(); i-- > 0; )
{
hash = hash * 31 + buffer.charAt( i );
}
return hash;
}
@Override
boolean eq( Node obj )
{
Literal that = (Literal) obj;
int len = buffer.length();
if ( that.buffer.length() != len )
{
return false;
}
for ( int i = 0; i < len; i++ )
{
if ( this.buffer.charAt( i ) != that.buffer.charAt( i ) )
{
return false;
}
}
return true;
}
@Override
Node append( String literal )
{
buffer.append( literal );
return this;
}
@Override
Node appendCodePoint( int cp )
{
buffer.appendCodePoint( cp );
return this;
}
@Override
public void write( Output output )
{
output.append( buffer );
}
@Override
void toString( Output result )
{
result.append( '\'' )
.append( buffer.toString().replace( "\r", "\\r" ).replace( "\n", "\\n" ).replace( "\t", "\\t" ) )
.append( '\'' );
}
@Override
void sExpression( Output output, int indent )
{
toString( output );
}
}
private static class Replacement extends Node
{
private final ProductionReplacement replacement;
private final T context;
private final Consumer defaults;
private Replacement(
Node parent, String name, ProductionReplacement replacement, T context, Consumer defaults )
{
super( parent, name );
this.replacement = replacement;
this.context = context;
this.defaults = defaults;
}
@Override
public void write( Output output )
{
replacement.replace( new ReplacementContext( this, output ) );
}
@Override
int hash()
{
return replacement.hashCode() * 31 + Objects.hashCode( context );
}
@Override
boolean eq( Node obj )
{
Replacement that = (Replacement) obj;
return Objects.equals( this.replacement, that.replacement ) &&
Objects.equals( this.context, that.context );
}
}
private static class ReplacementContext implements ProductionReplacement.Context
{
private final Replacement replacement;
private final Output output;
private ReplacementContext( Replacement replacement, Output output )
{
this.replacement = replacement;
this.output = output;
}
@Override
public Node node()
{
return replacement;
}
@Override
public void generateDefault()
{
Tree tree = new Tree( replacement.parent(), replacement.name() );
replacement.defaults.accept( tree );
tree.write( output );
}
@Override
public T context()
{
return replacement.context;
}
@Override
public void write( CharSequence str )
{
output.append( str );
}
@Override
public void write( int codePoint )
{
output.appendCodePoint( codePoint );
}
@Override
public Output output()
{
return output;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy