/**
* Copyright (c) 2014 by the original author or authors.
*
* This code 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 2.1 of the License, or (at your option) any later version.
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package ch.sdi.report;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import ch.sdi.core.impl.data.Dataset;
import ch.sdi.core.impl.data.Person;
import ch.sdi.report.ReportMsg.ReportType;
/**
* Global singleton for collecting ReportMsg items in order to render a report at the end of the
* data import.
* <p>
*
* @version 1.0 (20.11.2014)
* @author Heri
*/
@Component
public class SdiReporter
{
/** logger for this class */
private Logger myLog = LogManager.getLogger( SdiReporter.class );
private List<ReportMsg> myMessages = new ArrayList<ReportMsg>();
/**
* Constructor
*
*/
public SdiReporter()
{
super();
}
/**
* Clears all ReportMsg from internal memory
*/
public void reset()
{
myLog.info( "resetting " + this.getClass().getSimpleName() );
myMessages = new ArrayList<ReportMsg>();
}
/**
* Adds given ReportMsg to the internal memory
* <p>
* @param aMsg
*/
public void add( ReportMsg aMsg )
{
// Since wie are already in a call from a logger there would be an error entry if we call
// the logger again ("Recursive call to appender "). So log this asynchroneously:
CompletableFuture
.supplyAsync(() -> "adding a message" )
.thenAcceptAsync( myLog::trace );
myMessages.add( aMsg );
}
/**
* Renders the report
* <p>
* @return
*/
public String getReport()
{
myLog.debug( "Rendering the report" );
StringBuilder result = new StringBuilder();
DateFormat df = new SimpleDateFormat( "dd.MM.yyyy, HH:mm:ss" );
appendTitle( result, "Import " + df.format( new Date() ), "*" );
result.append( "\n" );
appendSimpleEntry( result,
"Source:",
findSimpleStringEntry( ReportType.COLLECTOR_CFG, "InputSource" ) );
result.append( "\n" );
Collection<ReportMsg> preparsedFiltered = findOfTypes( ReportType.PREPARSE_FILTER );
appendSummary( result, preparsedFiltered );
Collection<ReportMsg> parsed = findOfTypes( ReportType.COLLECTOR );
appendSummary( result, parsed );
Collection<ReportMsg> parsedProblems = findOfTypes( ReportType.COLLECTOR_PROBLEM );
appendSummary( result, parsedProblems );
Collection<ReportMsg> postparsedFiltered = findOfTypes( ReportType.POSTPARSE_FILTER );
appendSummary( result, postparsedFiltered );
Collection<ReportMsg> skippedNoEmail = findOfTypes( ReportType.SKIPPED_NO_EMAIL );
appendSummary( result, skippedNoEmail );
Collection<ReportMsg> processed = findOfTypes( ReportType.TARGET );
appendSummary( result, processed );
Collection<ReportMsg> processFailed = findOfTypes( ReportType.TARGET_PROBLEM );
appendSummary( result, processFailed );
result.append( "\n" );
appendProcessedPersons( result, processed );
result.append( "\n" );
appendFailedPersons( result, processFailed );
result.append( "\n" );
appendSkippedNoEmail( result, skippedNoEmail );
result.append( "\n" );
appendFiltered( result, postparsedFiltered );
result.append( "\n" );
appendTitle( result, "All collected report messages (unformatted)", "-" );
result.append( "" + myMessages );
return result.toString();
}
/**
* @param aResult
* @param aFiltered
*/
private void appendFiltered( StringBuilder aSb, Collection<ReportMsg> aFiltered )
{
appendTitle( aSb, "Filtered while collecting the raw data", "-" );
aFiltered.stream()
.filter( msg ->
(
msg.getKey().equals( "Skpped Persons (no mail address)" ) )
&&
msg.getValue() instanceof Collection
)
.map( msg -> Collection.class.cast( msg.getValue() ) )
.forEach( list -> appendDatasetList( aSb, list ) );
}
/**
* @param aSb
* @param aList
* @return
*/
private void appendDatasetList( StringBuilder aSb, Collection<?> aList )
{
aList.stream()
.filter( p -> p instanceof Dataset )
.map( p -> Dataset.class.cast( p ) )
.forEach
( p -> appendDataset( aSb, p ) );
}
/**
* @param aSb
* @param aP
* @return
*/
private void appendDataset( StringBuilder aSb, Dataset aDataset )
{
aSb.append( " " )
.append( aDataset )
.append( "\n" );
}
/**
* @param aResult
* @param aSkippedNoEmail
*/
private void appendSkippedNoEmail( StringBuilder aSb, Collection<ReportMsg> aSkippedNoEmail )
{
appendTitle( aSb, "Skipped because there is no email address", "-" );
aSkippedNoEmail.stream()
.filter( msg ->
(
msg.getKey().equals( "Skpped Persons (no mail address)" ) )
&&
msg.getValue() instanceof Collection
)
.map( msg -> Collection.class.cast( msg.getValue() ) )
.forEach( list -> appendPersonList( aSb, list ) );
}
/**
* @param aResult
* @param aProcessFailed
*/
private void appendFailedPersons( StringBuilder aSb, Collection<ReportMsg> aProcessFailed )
{
appendTitle( aSb, "Failed during processing", "-" );
aProcessFailed.stream()
.filter( msg ->
(
msg.getKey().equals( "FailedPersons" ) )
&&
msg.getValue() instanceof Collection
)
.map( msg -> Collection.class.cast( msg.getValue() ) )
.forEach( list -> appendPersonList( aSb, list ) );
}
/**
* @param aResult
* @param aProcessed
*/
private void appendProcessedPersons( StringBuilder aSb, Collection<ReportMsg> aProcessed )
{
appendTitle( aSb, "Processed Persons", "-" );
aProcessed.stream()
.filter( msg ->
(
msg.getKey().equals( "ProcessedPersons" ) )
&&
msg.getValue() instanceof Collection
)
.map( msg -> Collection.class.cast( msg.getValue() ) )
.forEach( list -> appendPersonList( aSb, list ) );
aSb.append( "\n" );
appendTitle( aSb, "Persons already in target platform (duplicate)", "-" );
aProcessed.stream()
.filter( msg ->
(
msg.getKey().equals( "DuplicatePersons" ) )
&&
msg.getValue() instanceof Collection
)
.map( msg -> Collection.class.cast( msg.getValue() ) )
.forEach( list -> appendPersonList( aSb, list ) );
}
private void appendPersonList( StringBuilder aSb, Collection<?> list )
{
list.stream()
.filter( p -> p instanceof Person )
.map( p -> Person.class.cast( p ) )
.forEach
( p -> appendSimplePerson( aSb, p ) );
}
private void appendSimplePerson( StringBuilder aSb, Person<?> p )
{
aSb.append( " " )
.append( p.getEMail() )
.append( " (" )
.append( p.getGivenname() )
.append( " " )
.append( p.getFamilyName() )
.append( ")\n" );
}
/**
* @param aResult
* @param aMessages
*/
private void appendSummary( StringBuilder aSb, Collection<ReportMsg> aMessages )
{
aMessages.forEach( msg ->
{
Object value = msg.getValue();
if ( value instanceof Collection )
{
value = ((Collection<?>) value).size();
}
appendSimpleEntry( aSb,
msg.getKey(),
value );
} );
}
/**
*
*/
private void appendSimpleEntry( StringBuilder aSb, String aName, Object aValue )
{
aSb.append( aName ).append( ": " ).append( aValue ).append( "\n" );
}
/**
* @param aResult
* @param aTitle
* @param aUnderlineChar
*/
private void appendTitle( StringBuilder aSb, String aTitle, String aUnderlineChar )
{
aSb.append( aTitle ).append( "\n" );
for ( int i = 0; i < aTitle.length(); i++ )
{
aSb.append( aUnderlineChar );
}
aSb.append( "\n" );
}
private String findSimpleStringEntry( ReportType aReportType, String aKey )
{
return findSimpleEntry( aReportType, aKey, String.class );
}
/**
* @param aCollector
* @param aString
* @return
*/
private <T> T findSimpleEntry( ReportType aReportType, String aKey, Class<T> aClass )
{
Collection<ReportMsg> messages = findOfTypes( aReportType );
for ( ReportMsg msg : messages )
{
if ( msg.getKey().equals( aKey ) )
{
Object o = msg.getValue();
if ( aClass.isInstance( o ) )
{
return aClass.cast( o );
} // if aClass.isInstance( obj )
myLog.warn( "Entry found for report type " + aReportType + " and key " + aKey
+ ", but not of desired type! Entry: " + o.getClass().getName()
+ "; expected: " + aClass.getName() );
return null;
} // if key.equals( aKey )
}
myLog.warn( "No entry found for report type " + aReportType + " and key " + aKey );
return null;
}
/**
* @param aReportType
* @return
*/
private Collection<ReportMsg> findOfTypes( ReportType aReportType )
{
return myMessages.stream()
.filter( msg -> msg.getType() == aReportType )
.collect( Collectors.toSet() );
}
}