/** * Copyright 2007 The JA-SIG Collaborative. All rights reserved. * See license distributed with this file and * available online at http://www.uportal.org/license.html */ package org.jasig.portal.io; import java.io.IOException; import java.io.Writer; import org.jasig.portal.ChannelManager; import org.jasig.portal.layout.IUserLayoutManager; /** * Provides for streaming token replacement with a character stream. * * @author Eric Dalquist * @version $Revision$ */ public class ChannelTitleIncorporationWiterFilter extends Writer { public static final String TITLE_TOKEN_PREFIX = "UP:CHANNEL_TITLE-{"; public static final int MAX_CHANNEL_ID_LENGTH = 32; public static final String TITLE_TOKEN_SUFFIX = "}"; private final StringBuilder prefixBuffer = new StringBuilder(TITLE_TOKEN_PREFIX.length()); private final StringBuilder channelIdBuffer = new StringBuilder(MAX_CHANNEL_ID_LENGTH); private final StringBuilder suffixBuffer = new StringBuilder(TITLE_TOKEN_SUFFIX.length()); private final char[] resetBuffer = new char[TITLE_TOKEN_PREFIX.length() + MAX_CHANNEL_ID_LENGTH + TITLE_TOKEN_SUFFIX.length()]; private final Writer wrappedWriter; private final ChannelManager channelManager; private final IUserLayoutManager userLayoutManager; /** * @param wrappedWriter Writer to delegate writing to. * @param channelManager Used to load the dynamic channel title. * @param userLayoutManager Used to access the default title if no dynamic title is provided. */ public ChannelTitleIncorporationWiterFilter(Writer wrappedWriter, ChannelManager channelManager, IUserLayoutManager userLayoutManager) { this.wrappedWriter = wrappedWriter; this.channelManager = channelManager; this.userLayoutManager = userLayoutManager; } /** * Used for unit testing; */ ChannelTitleIncorporationWiterFilter(Writer wrappedWriter) { this.wrappedWriter = wrappedWriter; this.channelManager = null; this.userLayoutManager = null; } /* (non-Javadoc) * @see java.io.Writer#close() */ @Override public void close() throws IOException { if (this.prefixBuffer.length() > 0) { this.wrappedWriter.append(this.prefixBuffer); } if (this.channelIdBuffer.length() > 0) { this.wrappedWriter.append(this.channelIdBuffer); } if (this.suffixBuffer.length() > 0) { this.wrappedWriter.append(this.suffixBuffer); } this.wrappedWriter.close(); } /* (non-Javadoc) * @see java.io.Writer#flush() */ @Override public void flush() throws IOException { this.wrappedWriter.flush(); } /* (non-Javadoc) * @see java.io.Writer#write(char[], int, int) */ @Override public void write(char[] cbuf, int off, int len) throws IOException { for (int charIndex = off; charIndex < (off + len); charIndex++) { final char writerChar = cbuf[charIndex]; final int prefixBufferLength = this.prefixBuffer.length(); //Prefix buffer is full, must be looking for id or suffix if (TITLE_TOKEN_PREFIX.length() == prefixBufferLength) { final int suffixBufferLength = this.suffixBuffer.length(); //No id chars, there must always be at least one if (this.channelIdBuffer.length() == 0) { this.channelIdBuffer.append(writerChar); } //Something in the id buffer, check against suffix else { //Found a suffix character if (TITLE_TOKEN_SUFFIX.charAt(suffixBufferLength) == writerChar) { this.suffixBuffer.append(writerChar); //If the suffix buffer is full do the title replacement and clear the buffers. if (TITLE_TOKEN_SUFFIX.length() == this.suffixBuffer.length()) { final String channelId = this.channelIdBuffer.toString(); final String channelTitle = this.getChannelTitle(channelId); this.wrappedWriter.write(channelTitle); this.prefixBuffer.delete(0, this.prefixBuffer.length()); this.channelIdBuffer.delete(0, this.channelIdBuffer.length()); this.suffixBuffer.delete(0, this.suffixBuffer.length()); } } //Not a suffix character else { //channel id is too long, must not be a token, re-parse all but the first char of the prefix buffer final int channelIdBufferLength = this.channelIdBuffer.length(); if (MAX_CHANNEL_ID_LENGTH == channelIdBufferLength) { //Write the first char from the prefix out to the writer final char deadChar = this.prefixBuffer.charAt(0); this.wrappedWriter.write(deadChar); //Copy the rest of the prefix buffer so it can be parsed from the 2nd char on with a clean buffer this.prefixBuffer.getChars(1, prefixBufferLength, this.resetBuffer, 0); this.prefixBuffer.delete(0, prefixBufferLength); this.channelIdBuffer.getChars(0, channelIdBufferLength, this.resetBuffer, prefixBufferLength - 1); this.channelIdBuffer.delete(0, channelIdBufferLength); this.resetBuffer[channelIdBufferLength + prefixBufferLength - 1] = writerChar; this.write(this.resetBuffer, 0, channelIdBufferLength + prefixBufferLength); } //Chars in the suffix buffer, need to re-parse all be the first. else if (suffixBufferLength > 0) { //Write the first char from the suffix to the id buffer final char idChar = this.suffixBuffer.charAt(0); this.channelIdBuffer.append(idChar); //Copy the rest of the suffix buffer so it can be parsed from the 2nd char on with a clean buffer this.suffixBuffer.getChars(1, suffixBufferLength, this.resetBuffer, 0); this.suffixBuffer.delete(0, suffixBufferLength); this.resetBuffer[suffixBufferLength - 1] = writerChar; this.write(this.resetBuffer, 0, suffixBufferLength); } //Nothing in the suffix buffer, must be part of the id else { this.channelIdBuffer.append(writerChar); } } } } //Found a prefix char else if (TITLE_TOKEN_PREFIX.charAt(prefixBufferLength) == writerChar) { this.prefixBuffer.append(writerChar); } //Char doesn't match anything else { //Chars in the prefix buffer need to re-parse all but the first if (prefixBufferLength > 0) { //Write the first char from the prefix out to the writer final char deadChar = this.prefixBuffer.charAt(0); this.wrappedWriter.write(deadChar); //Copy the rest of the prefix buffer so it can be parsed from the 2nd char on with a clean buffer this.prefixBuffer.getChars(1, prefixBufferLength, this.resetBuffer, 0); this.prefixBuffer.delete(0, prefixBufferLength); this.resetBuffer[prefixBufferLength - 1] = writerChar; this.write(this.resetBuffer, 0, prefixBufferLength); } //Nothing in the prefix buffer, must be a normal char else { this.wrappedWriter.write(writerChar); } } } } /** * Get the channel title for the specified channel id. */ protected String getChannelTitle(String channelId) { //Used when testing if (this.channelManager == null) { return "Channel '" + channelId + "' Title"; } String title = this.channelManager.getChannelTitle(channelId); if (title == null) { title = this.userLayoutManager.getNode(channelId).getName(); } return title; } }