Skip to content

Commit

Permalink
Correctly serialize and indent anonymous nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
atextor committed Jan 26, 2021
1 parent c1366a7 commit 29d27de
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public class FormattingStyle {
WrappingStyle wrapListItems = WrappingStyle.FOR_LONG_LINES;

@Builder.Default
boolean firstPredicateInNewLIne = false;
boolean firstPredicateInNewLine = false;

@Builder.Default
boolean useAForRdfType = true;
Expand Down
101 changes: 70 additions & 31 deletions write/src/main/java/de/atextor/owlcli/write/TurtleFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.vavr.Tuple2;
import io.vavr.collection.HashMap;
import io.vavr.collection.HashSet;
import io.vavr.collection.List;
import io.vavr.collection.Map;
import io.vavr.collection.Set;
import io.vavr.collection.Stream;
Expand Down Expand Up @@ -81,13 +82,13 @@ public TurtleFormatter( final FormattingStyle style ) {
).thenComparing( RDFNode::toString );
}

private static Stream<Statement> statements( final Model model ) {
return Stream.ofAll( model::listStatements );
private static List<Statement> statements( final Model model ) {
return List.ofAll( model.listStatements().toList() );
}

private static Stream<Statement> statements( final Model model, final Resource subject, final Property predicate,
final RDFNode object ) {
return Stream.ofAll( () -> model.listStatements( subject, predicate, object ) );
private static List<Statement> statements( final Model model, final Resource subject, final Property predicate,
final RDFNode object ) {
return List.ofAll( model.listStatements( subject, predicate, object ).toList() );
}

@Override
Expand All @@ -113,17 +114,20 @@ public String apply( final Model model ) {
Comparator.comparing( statement -> statement.getSubject().isURIResource() ?
prefixMapping.shortForm( statement.getSubject().getURI() ) : statement.getSubject().toString() );

final Stream<Statement> wellKnownSubjects = Stream.ofAll( style.subjectOrder ).flatMap( subjectType ->
final List<Statement> wellKnownSubjects = List.ofAll( style.subjectOrder ).flatMap( subjectType ->
statements( model, null, RDF.type, subjectType ).sorted( subjectComparator ) );
final Stream<Statement> otherSubjects = statements( model )
final List<Statement> otherSubjects = statements( model )
.filter( statement -> !statement.getPredicate().equals( RDF.type ) )
.sorted( subjectComparator );
final Stream<Statement> statements = wellKnownSubjects.appendAll( otherSubjects );
final List<Statement> statements = wellKnownSubjects.appendAll( otherSubjects )
.filter( statement -> !( statement.getSubject().isAnon()
&& model.contains( null, null, statement.getSubject() ) ) );

final State finalState = statements
.map( Statement::getSubject )
.foldLeft( prefixesWritten, ( state, resource ) ->
writeSubject( resource, state.withIndentationLevel( 0 ) ) );
resource.isURIResource() ? writeSubject( resource, state.withIndentationLevel( 0 ) ) :
writeAnonymousResource( resource, state.withIndentationLevel( 0 ) ) );

LOG.debug( "Written {} resources, with {} named anonymous resources", finalState.visitedResources.size(),
finalState.identifiedAnonymousResources.size() );
Expand Down Expand Up @@ -207,37 +211,61 @@ private State writeComma( final State state ) {
continuationIndent( state.indentationLevel ), state );
}

private State writeSemicolon( final State state ) {
return writeDelimiter( ";", style.beforeSemicolon, style.afterSemicolon,
private State writeSemicolon( final State state, final boolean omitLineBreak,
final boolean omitSpaceBeforeSemicolon ) {
final FormattingStyle.GapStyle beforeSemicolon = omitSpaceBeforeSemicolon ? FormattingStyle.GapStyle.NOTHING :
style.beforeSemicolon;
final FormattingStyle.GapStyle afterSemicolon = omitLineBreak ? FormattingStyle.GapStyle.NOTHING :
style.afterSemicolon;
return writeDelimiter( ";", beforeSemicolon, afterSemicolon,
style.alignPredicates ? "" : indent( state.indentationLevel ), state );
}

private State writeDot( final State state ) {
return writeDelimiter( ".", style.beforeDot, style.afterDot, "", state );
private State writeDot( final State state, final boolean omitSpaceBeforeDot ) {
final FormattingStyle.GapStyle beforeDot = omitSpaceBeforeDot ? FormattingStyle.GapStyle.NOTHING :
style.beforeDot;
return writeDelimiter( ".", beforeDot, style.afterDot, "", state );
}

private State writeOpeningSquareBracket( final State state ) {
final FormattingStyle.GapStyle beforeBracket = state.indentationLevel > 0 ? style.beforeOpeningSquareBracket :
FormattingStyle.GapStyle.NOTHING;
return writeDelimiter( "[", beforeBracket, style.afterOpeningSquareBracket,
indent( state.indentationLevel ), state );
}

private State writeClosingSquareBracket( final State state ) {
return writeDelimiter( "]", style.beforeClosingSquareBracket, style.afterClosingSquareBracket,
indent( state.indentationLevel ), state );
}

private boolean isList( final RDFNode node, final State state ) {
return node.equals( RDF.nil ) ||
( node.isResource() && state.model.contains( node.asResource(), RDF.rest, (RDFNode) null ) );
}

private State writeResource( final Resource resource, final State state ) {
if ( isList( resource, state ) ) {
return writeList( resource, state );
}
if ( resource.isURIResource() ) {
if ( resource.equals( RDF.nil ) || state.model.contains( resource, RDF.rest, (RDFNode) null ) ) {
return writeList( resource, state );
}

return writeUriResource( resource, state );
}
return writeAnonymousResource( resource, state );
}

private State writeList( final Resource resource, final State state ) {
return state;
return state.write( "()" );
}

private State writeAnonymousResource( final Resource resource, final State state ) {
if ( !state.model.contains( resource, null, (RDFNode) null ) ) {
return state.write( "[]" );

}

return state.write( "[...]" );
final State afterOpeningSquareBracket = writeOpeningSquareBracket( state );
final State afterContent = writeSubject( resource, afterOpeningSquareBracket ).removeIndentationLevel();
return writeClosingSquareBracket( afterContent );
}

private State writeUriResource( final Resource resource, final State state ) {
Expand Down Expand Up @@ -297,9 +325,12 @@ private State writeSubject( final Resource resource, final State state ) {
// indent
final State indentedSubject = state.write( indent( state.indentationLevel ) );
// subject
final State stateWithSubject = writeResource( resource, indentedSubject )
.withVisitedResource( resource );
final State gapAfterSubject = style.firstPredicateInNewLine ? stateWithSubject : stateWithSubject.write( " " );
final State stateWithSubject =
resource.isURIResource() ? writeResource( resource, indentedSubject ).withVisitedResource( resource ) :
indentedSubject.withVisitedResource( resource );

final State gapAfterSubject = style.firstPredicateInNewLine || resource.isAnon() ?
stateWithSubject : stateWithSubject.write( " " );

final int predicateAlignment = style.firstPredicateInNewLine ? style.indentSize : gapAfterSubject.alignment;

Expand All @@ -311,7 +342,7 @@ private State writeSubject( final Resource resource, final State state ) {
.ofAll( properties )
.sorted( state.predicateOrder )
.zipWithIndex()
.foldLeft( stateWithSubject.indentedOnce(), ( currentState, indexedProperty ) -> {
.foldLeft( stateWithSubject.addIndentationLevel(), ( currentState, indexedProperty ) -> {
final Property property = indexedProperty._1();
final int index = indexedProperty._2();
final boolean firstProperty = index == 0;
Expand Down Expand Up @@ -348,17 +379,21 @@ private State writeProperty( final Resource subject, final Property predicate, f
final boolean lastObject = index == objects.size() - 1;

final State predicateWritten = useComma ? currentState :
writeProperty( predicate, currentState ).write( " " );
writeProperty( predicate, currentState );

final State spaceWritten = ( !object.isAnon() || isList( object, predicateWritten ) ) && !useComma ?
predicateWritten.write( " " ) :
predicateWritten;

final State objectWritten = writeRdfNode( object, predicateWritten );
final State objectWritten = writeRdfNode( object, spaceWritten );
if ( useComma && !lastObject ) {
return writeComma( objectWritten );

}
if ( lastProperty && lastObject ) {
return writeDot( objectWritten ).newLine();

if ( lastProperty && lastObject && objectWritten.indentationLevel == 1 ) {
return writeDot( objectWritten, object.isAnon() ).newLine();
}
return writeSemicolon( objectWritten );
return writeSemicolon( objectWritten, lastProperty && lastObject, object.isAnon() );
} );
}

Expand Down Expand Up @@ -395,10 +430,14 @@ public State withVisitedResource( final Resource visitedResource ) {
return withVisitedResources( visitedResources.add( visitedResource ) );
}

public State indentedOnce() {
public State addIndentationLevel() {
return withIndentationLevel( indentationLevel + 1 );
}

public State removeIndentationLevel() {
return withIndentationLevel( indentationLevel - 1 );
}

public State newLine() {
return write( endOfLine ).withAlignment( 0 );
}
Expand Down

0 comments on commit 29d27de

Please sign in to comment.