/****************************************************************************** * Copyright (c) 2016 Oracle * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Konstantin Komissarchik - initial implementation and ongoing maintenance ******************************************************************************/ package org.eclipse.sapphire.modeling; import static org.eclipse.sapphire.modeling.util.MiscUtil.equal; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Comparator; import java.util.Iterator; import java.util.SortedSet; import org.eclipse.sapphire.LocalizableText; import org.eclipse.sapphire.Text; import org.eclipse.sapphire.util.SortedSetFactory; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public final class Status { public static final String TYPE_MISC_OK = "Sapphire.Miscellaneous.Ok"; public static final String TYPE_MISC_PROBLEM = "Sapphire.Miscellaneous.Problem"; @Text( "error" ) private static LocalizableText defaultErrorMessage; @Text( "ok" ) private static LocalizableText defaultOkMessage; static { LocalizableText.init( Status.class ); } private static final Status OK_STATUS = new Status( Severity.OK, TYPE_MISC_OK, defaultOkMessage.text(), null, SortedSetFactory.<Status>empty() ); public static Status createOkStatus() { return OK_STATUS; } public static Status createWarningStatus( final String message ) { return createStatus( Severity.WARNING, message ); } public static Status createErrorStatus( final String message ) { return createErrorStatus( message, null ); } public static Status createErrorStatus( final Throwable exception ) { if( exception == null ) { throw new IllegalArgumentException(); } final String text = exception.getMessage(); final StringBuilder msg = new StringBuilder(); msg.append( exception.getClass().getSimpleName() ); if( text != null && text.length() != 0 ) { msg.append( ": " ); msg.append( text ); } return createErrorStatus( msg.toString(), exception ); } public static Status createErrorStatus( final String message, final Throwable exception ) { return createStatus( Severity.ERROR, message, exception ); } public static Status createStatus( final Severity severity, final String message, final Throwable exception ) { return factoryForLeaf().severity( severity ).message( message ).exception( exception ).create(); } public static Status createStatus( final Severity severity, final String message ) { return createStatus( severity, message, null ); } public static LeafStatusFactory factoryForLeaf() { return new LeafStatusFactory(); } public static CompositeStatusFactory factoryForComposite() { return new CompositeStatusFactory(); } private final Severity severity; private final String type; private final String message; private final Throwable exception; private final SortedSet<Status> children; private Status( final Severity severity, final String type, final String message, final Throwable exception, final SortedSet<Status> children ) { this.severity = severity; this.type = ( type == null ? ( severity == Severity.OK ? TYPE_MISC_OK : TYPE_MISC_PROBLEM ) : type ); this.message = message; this.exception = exception; this.children = children; } public boolean ok() { return ( this.severity == Severity.OK ); } public Severity severity() { return this.severity; } public String type() { return this.type; } public String message() { return this.message; } public Throwable exception() { return this.exception; } public SortedSet<Status> children() { return this.children; } public boolean contains( final String type ) { if( equal( this.type, type ) ) { return true; } for( Status child : this.children ) { if( child.contains( type ) ) { return true; } } return false; } @Override public boolean equals( final Object obj ) { if( obj == this ) { return true; } if( obj instanceof Status ) { final Status st = (Status) obj; if( st.severity() == severity() && st.children().size() == children().size() && st.exception() == exception() && st.message().equals( message() ) ) { for( Iterator<Status> itr1 = st.children().iterator(), itr2 = children().iterator(); itr1.hasNext(); ) { if( ! itr1.next().equals( itr2.next() ) ) { return false; } } return true; } } return false; } @Override public int hashCode() { return severity().code() ^ message().hashCode(); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append( severity().name() ); buf.append( " : " ); buf.append( message() ); final Throwable e = exception(); if( e != null ) { buf.append( System.getProperty( "line.separator" ) ); final StringWriter sw = new StringWriter(); e.printStackTrace( new PrintWriter( sw ) ); buf.append( sw.toString() ); } return buf.toString(); } public enum Severity { OK( 0 ), WARNING( 1 ), ERROR( 2 ); private int code; private Severity( final int code ) { this.code = code; } public int code() { return this.code; } } public static final class LeafStatusFactory { private Severity severity; private String type; private String message; private Throwable exception; private LeafStatusFactory() { // No direct public instantiation. Use factoryForLeaf() method instead. } public LeafStatusFactory severity( final Severity severity ) { this.severity = severity; return this; } public LeafStatusFactory type( final String type ) { this.type = type; return this; } public LeafStatusFactory message( final String message ) { this.message = message; return this; } public LeafStatusFactory exception( final Throwable exception ) { this.exception = exception; return this; } public Status create() { if( this.severity == null ) { throw new IllegalStateException(); } if( this.message == null ) { throw new IllegalStateException(); } return new Status( this.severity, this.type, this.message, this.exception, SortedSetFactory.<Status>empty() ); } } public static final class CompositeStatusFactory { private final SortedSetFactory<Status> children = SortedSetFactory.start( StatusComparator.INSTANCE ); private CompositeStatusFactory() { // No direct public instantiation. Use factoryForComposite() method instead. } public CompositeStatusFactory merge( final Status status ) { if( status != null ) { final SortedSet<Status> children = status.children(); if( children.isEmpty() ) { final Severity sev = status.severity(); if( sev != Severity.OK ) { this.children.add( status ); } } else { for( Status st : children ) { merge( st ); } } } return this; } public Status create() { final int count = this.children.size(); if( count == 0 ) { return createOkStatus(); } else { final Status first = this.children.first(); if( count == 1 ) { return first; } else { return new Status( first.severity(), TYPE_MISC_PROBLEM, first.message(), first.exception(), this.children.result() ); } } } } private static final class StatusComparator implements Comparator<Status> { private static final StatusComparator INSTANCE = new StatusComparator(); public int compare( final Status x, final Status y ) { int result = y.severity.code() - x.severity.code(); if( result == 0 ) { result = x.message.compareTo( y.message ); if( result == 0 ) { result = x.type.compareTo( y.type ); if( result == 0 ) { if( x.exception != y.exception ) { if( x.exception == null ) { result = -1; } else if( y.exception == null ) { result = 1; } else { result = x.exception.getClass().getName().compareTo( y.exception.getClass().getName() ); if( result == 0 ) { result = System.identityHashCode( x.exception ) - System.identityHashCode( y.exception ); } } } if( result == 0 ) { result = x.children.size() - y.children.size(); if( result == 0 ) { final Iterator<Status> xChildren = x.children.iterator(); final Iterator<Status> yChildren = y.children.iterator(); while( xChildren.hasNext() && result == 0 ) { result = compare( xChildren.next(), yChildren.next() ); } } } } } } return result; } } }