/*
* [RSSSortableItem.java]
*
* Summary: One RSS item with attached feedName and PublishDate for sorting keys.
*
* Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
*
* Licence: This software may be copied and used freely for any purpose but military.
* http://mindprod.com/contact/nonmil.html
*
* Requires: JDK 1.8+
*
* Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
*
* Version History:
* 1.8 2009-02-06 include go package in ZIP bundle.
*/
package com.mindprod.htmlmacros.support;
import com.mindprod.common18.BigDate;
import com.mindprod.common18.EIO;
import com.mindprod.common18.ST;
import com.mindprod.entities.DeEntifyStrings;
import com.mindprod.entities.EntifyStrings;
import com.mindprod.fastcat.FastCat;
import com.mindprod.htmlmacros.macro.Global;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Locale;
import java.util.TimeZone;
import static com.mindprod.htmlmacros.macro.Global.configuration;
import static java.lang.System.*;
/**
* One RSS item with attached feedName and PublishDate for sorting keys.
*
* @author Roedy Green, Canadian Mind Products
* @version 1.8 2009-02-06 include go package in ZIP bundle.
* @since 2009
*/
public final class RSSSortableItem
{
private static final Charset ISO_8859_1 = Charset.forName( "ISO-8859-1" );
/**
* mask for: "Fri, 08 Aug 2008 13:59:59" publish date.
*/
@SuppressWarnings( { "StringWithMistakes" } )
/**
format for publish date of individual item */
private static final SimpleDateFormat PUBLISH_DATE_SDF;
static
{
PUBLISH_DATE_SDF =
new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US );
PUBLISH_DATE_SDF.setTimeZone( TimeZone.getTimeZone( "GMT" ) );// standard does not support UTC
}
/**
* long description. May contain entities and HTML tags, which will later be stripped if need be.
*/
private String description;
/**
* name of the feed
*/
private String feedName;
/**
* the link to this item, either webroot relative jgloss/xxx.html or absolute htttp://somesite.com/place.html
*/
private String link;
/**
* Short title. May contain entities and HTML tags, which will later be stripped if need be.
*/
private String title;
/**
* date item published as an BigDate ordinal
*/
private int publishDate;
public RSSSortableItem( String feedName,
int publishDate,
String link,
String title,
String description )
{
this.feedName = feedName.intern();// there will be many duplicates.
this.publishDate = publishDate;
this.link = link;
this.title = title;
this.description = description;
}
/**
* constructor used by read
*/
private RSSSortableItem()
{
}
/**
* read in one item from a binary logging file
*
* @param rssBinaryLog DataOutputStream to write to
*
* @return RSS item read from binary log file.
* @throws IOException if trouble reading the binary log file.
*/
static RSSSortableItem read( DataInputStream rssBinaryLog ) throws IOException
{
RSSSortableItem n = new RSSSortableItem();
// read as counted string.
n.feedName = rssBinaryLog.readUTF().intern();
n.publishDate = rssBinaryLog.readInt();
n.link = rssBinaryLog.readUTF();
n.title = rssBinaryLog.readUTF();
n.description = rssBinaryLog.readUTF();
return n;
}
/**
* build XML for one RSS-2 feed item, all on one line to make sort easier.
*
* @return one RSS-2 feed item xml in an ... sandwich.
*/
String asRSSxml()
{
final FastCat sb = new FastCat( 20 );
sb.append( "\n" );
sb.append( " " );
// html -> flat -> XML entities.
sb.append( EntifyStrings.entifyXML( DeEntifyStrings.flattenXML( title ) ) );
sb.append( "\n" );
sb.append( " " );
if ( link.startsWith( "http://" ) )
{
sb.append( link );
}
else
{
// only check for existence of local files.
// when checking existence, the #xxxx on the end is irrelevant.
final int position = link.lastIndexOf( '#' );
final String shouldExist = position >= 0 ? link.substring( 0, position ) : link;
final File f = new File( configuration.getLocalWebrootWithSlashes(), shouldExist );
if ( !f.exists() )
{
throw new IllegalArgumentException(
"RSS macro uPath link "
+ link
+ " ["
+ EIO.getCanOrAbsPath( f )
+ "] does not exist."
);
}
// need an absolute, not a relative link. Can't use completeLink
sb.append( "http://" );
sb.append( configuration.getWebsiteDomain() );
sb.append( "/" );
sb.append( link );// possibly with #xxx on the end.
}
sb.append( "\n" );
sb.append( " " );
// we need a globally unique id, not just unique within our website, our feed or this current file.
// So we throw a lot of branches into the chipper.
sb.append( ( globallyUniqueID() ) );
sb.append( "\n" );
sb.append( " " );
sb.append( PUBLISH_DATE_SDF.format( new BigDate( publishDate ).getLocalDate() ) );
sb.append( "\n" );
sb.append( " " );
// html -> flat -> XML entities.
sb.append( EntifyStrings.entifyXML( DeEntifyStrings.flattenXML( description ) ) );
sb.append( "\n" );
sb.append( "\n" );
return sb.toString();
}
/**
* calculate an MD5 digest on crucial fields, like a heavy duty hashCode
*
* @return globally unique hex STring about 256 bytes long.
*/
private String globallyUniqueID()
{
try
{
/* throw name of website, name of feed, link, title in to the grinder to get a unique 128 byte result */
MessageDigest md = MessageDigest.getInstance( "MD5" );
md.update( configuration.getWebsiteDomain().getBytes( ISO_8859_1 ) );
md.update( link.getBytes( ISO_8859_1 ) );
md.update( title.getBytes( ISO_8859_1 ) );
// feed in four bytes of publishDate in big endian order
for ( int shift = 24; shift >= 0; shift -= 8 )
{
md.update( ( byte ) ( publishDate >>> shift & 0xff ) );
}
// resulting digest will 16 bytes, 256 bits, 32 hex digits long.
byte[] digest = md.digest();
/* convert to a 32-digit hex string */
final FastCat sb = new FastCat( digest.length );
for ( byte b : digest )
{
sb.append( ST.toLZHexString( b & 0xff, 2 ) );
}
return sb.toString();
}
catch ( Exception e )
{
err.println( "Feed fails because no MD5 support." );
System.exit( 1 );
}
return "";// can't happen.
}
/**
* write out he current item to a binary logging file
*
* @param rssBinaryLog DataOutputStream to write to
*
* @throws IOException if trouble writing binary log
*/
void write( DataOutputStream rssBinaryLog ) throws IOException
{
// write as counted strings
rssBinaryLog.writeUTF( feedName );
rssBinaryLog.writeInt( publishDate );
rssBinaryLog.writeUTF( link );
rssBinaryLog.writeUTF( title );
rssBinaryLog.writeUTF( description );
}
/**
* get the item as formatted HTML with
...
sandwich.
*
* @param fileBeingProcessed the file currently being processed.
*
* @return one RSS-2 feed item in human readable HTML as a
...
*/
public String asHTML( File fileBeingProcessed )
{
final FastCat sb = new FastCat( 5 );
sb.append( "
\n" );
return sb.toString();
}
public String getFeedName()
{
return feedName;
}
public int getPublishDate()
{
return publishDate;
}
/**
* get the title of the time suitable for embedding in an HTML comment..
*
* @return one RSS-2 feed item in human readable HTML
*/
public String titleInsideComment()
{
// we will embed this in an HTML comment.
// So we don't want tags and we don't want high acscii. So wo strip tags, but not entities.
return DeEntifyStrings.stripHTMLTags( title );
}
/**
* Sort RSS items by descending date, feeds mixed.
*
* Defines an alternate sort order for RSSSortableItem.
*
* @version 1.0 2009-05-22 - initial release
* @since 2009-05-22
*/
static class ByDescendingDate implements Comparator
{
/**
* Sort RSS items by descending date, feeds mixed.
* Defines an alternate sort order for RSSSortableItem.
* Compare two RSSSortableItem Objects.
* Compares descending publishDate.
* Informally, returns (a-b), or +ve if a is more positive than b.
*
* @param a first RSSSortableItem to compare
* @param b second RSSSortableItem to compare
*
* @return +ve if a>b, 0 if a==b, -ve if a<b
*/
public final int compare( RSSSortableItem a, RSSSortableItem b )
{
return b.publishDate - a.publishDate;
}
}
/**
* Sort RSS items by feed, by descending date.
*
* Defines an alternate sort order for RSSSortableItem.
*
* @version 1.0 2009-05-22 - initial release
* @since 2009-05-22
*/
static class ByFeedByDate implements Comparator
{
/**
* Sort RSS items by feed, by descending date.
* Defines an alternate sort order for RSSSortableItem.
* Compare two RSSSortableItem Objects.
* Compares feedName then descending publishDate.
* Informally, returns (a-b), or +ve if a is more positive than b.
*
* @param a first RSSSortableItem to compare
* @param b second RSSSortableItem to compare
*
* @return +ve if a>b, 0 if a==b, -ve if a<b
*/
public final int compare( RSSSortableItem a, RSSSortableItem b )
{
int diff = a.feedName.compareTo( b.feedName );
if ( diff != 0 )
{
return diff;
}
return b.publishDate - a.publishDate;
}
}
}