/* * [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( "
  • " ); sb.append( Tools.completeLink( link, title, Global.assignCSSClasses.assignCSSClass( link, fileBeingProcessed ), fileBeingProcessed ) );// not flattened. sb.append( ": " ); sb.append( description );// not flattened. 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; } } }