プログラマメモ2 - programmer no memo2

javassistを使って、既存のクラスのメソッドを置き換えるための手習い。 2008/02/20

狙いはjaspereportsのnet.sf.jasperreports.engine.export.renderParagraphの処理を変更したいということ。

対象としているのは、2.0.4
使用しているjavassistのバージョンは、3.6.0.GA

手順
(1)
まず、renderParagraphメソッドから中身をごっそりファイルにしておく。
(2)
1.でとったテキストを修正する、クラスはパッケージまでつけて、メソッドの引数は$1,$2,$3...としておく。
あと、きちんと確認がとれてないのですが、どうもcaseに他のクラスの定数をかくとどうもだめっぽい。これはあとで確認します※
(3)
javassistを使ってsetBodyして、toClassして凍らせる。このときにエラーがでないことをめざす。

(1)

{
AttributedCharacterIterator paragraph = null;

if (lastParagraphText == null)
{
paragraph =
new AttributedString(
" ",
new AttributedString(
allParagraphs,
lastParagraphStart,
lastParagraphStart + 1
).getIterator().getAttributes()
).getIterator();
}
else
{
paragraph =
new AttributedString(
allParagraphs,
lastParagraphStart,
lastParagraphStart + lastParagraphText.length()
).getIterator();
}

LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, LINE_BREAK_FONT_RENDER_CONTEXT);//grx.getFontRenderContext()

while (lineMeasurer.getPosition() < paragraph.getEndIndex() && !isMaxHeightReached)
{
//eugene fix - start
int startIndex = lineMeasurer.getPosition();
//eugene fix - end

TextLayout layout = lineMeasurer.nextLayout(formatWidth);

if (isMinimizePrinterJobSize)
{
//eugene fix - start
AttributedString tmpText =
new AttributedString(
paragraph,
startIndex,
startIndex + layout.getCharacterCount()
);
layout = new TextLayout(tmpText.getIterator(), grx.getFontRenderContext());
//eugene fix - end
}

float lineHeight = lineSpacingFactor *
maxFontSizeFinder.findMaxFontSize(
new AttributedString(
paragraph,
startIndex,
startIndex + layout.getCharacterCount()
).getIterator(),
fontSize
);

if (drawPosY + lineHeight <= textHeight)
{
drawPosY += lineHeight;

switch (horizontalAlignment)
{
case JRAlignment.HORIZONTAL_ALIGN_JUSTIFIED :
{
if (layout.isLeftToRight())
{
drawPosX = 0;
}
else
{
drawPosX = formatWidth - layout.getAdvance();
}
if (lineMeasurer.getPosition() < paragraph.getEndIndex())
{
layout = layout.getJustifiedLayout(formatWidth);
}

break;
}
case JRAlignment.HORIZONTAL_ALIGN_RIGHT :
{
drawPosX = formatWidth - layout.getAdvance();
break;
}
case JRAlignment.HORIZONTAL_ALIGN_CENTER :
{
drawPosX = (formatWidth - layout.getAdvance()) / 2;
break;
}
case JRAlignment.HORIZONTAL_ALIGN_LEFT :;
default :
{
drawPosX = 0;
}
}

draw(layout);
}
else
{
isMaxHeightReached = true;
}
}
}


(2)加工するためのコード
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class FileA {
public static void main(String[] args) throws IOException {

InputStream stream = FileA.class.getResourceAsStream("a.txt");
BufferedReader reader = new BufferedReader(
new InputStreamReader(stream));
String s = null;
while ((s = reader.readLine()) != null) {
String ss = s.replaceFirst("//.*$", "");
ss = ss.replaceAll("\"", "\\\\\"");

ss = ss.replaceAll("JRAlignment\\.HORIZONTAL_ALIGN_LEFT", "1");
ss = ss.replaceAll("JRAlignment\\.HORIZONTAL_ALIGN_CENTER", "2");
ss = ss.replaceAll("JRAlignment\\.HORIZONTAL_ALIGN_RIGHT", "3");
ss = ss.replaceAll("JRAlignment\\.HORIZONTAL_ALIGN_JUSTIFIED", "4");

ss = ss.replaceAll("JRAlignment", "net.sf.jasperreports.engine.JRAlignment");
ss = ss.replaceAll("LineBreakMeasurer", " java.awt.font.LineBreakMeasurer");
ss = ss.replaceAll("TextLayout", "java.awt.font.TextLayout");
ss = ss.replaceAll("AttributedCharacterIterator", " java.text.AttributedCharacterIterator");
ss = ss.replaceAll("AttributedString", "java.text.AttributedString");
ss = ss.replaceAll("JRAlignment", "net.sf.jasperreports.engine.JRAlignment");

ss = ss.replaceAll("allParagraphs", "\\$1");
ss = ss.replaceAll("lastParagraphStart", "\\$2");
ss = ss.replaceAll("lastParagraphText", "\\$3");
ss = ss.replaceAll("LINE_BREAK_FONT_RENDER_CONTEXT", "net.sf.jasperreports.engine.export.TextRenderer.LINE_BREAK_FONT_RENDER_CONTEXT");

// java.awt.Graphics2D;
// java.awt.font.FontRenderContext;
// java.awt.font.LineBreakMeasurer;
// java.awt.font.TextLayout;
// java.text.AttributedCharacterIterator;
// java.text.AttributedString;
// java.util.StringTokenizer;
//
// net.sf.jasperreports.engine.JRAlignment;
// net.sf.jasperreports.engine.util.JRProperties;
// net.sf.jasperreports.engine.util.JRStyledText;
// net.sf.jasperreports.engine.util.MaxFontSizeFinder;

System.out.printf("buffer.append(\"%s\");%n", ss);
}

}

}


(3)javassistを使ってメソッドの処理を変更。
import net.sf.jasperreports.engine.export.TextRenderer;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

public class TestJavassist {

public static void main(String[] args) throws NotFoundException,
CannotCompileException {
a();
TextRenderer textRenderer = new TextRenderer(false);
}

static void a() throws NotFoundException, CannotCompileException {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("net.sf.jasperreports.engine.export.TextRenderer");
CtMethod method = cc.getDeclaredMethod("renderParagraph");
StringBuffer buffer = new StringBuffer();
buffer.append(" {");
buffer.append(" java.text.AttributedCharacterIterator paragraph = null;");
buffer.append(" ");
buffer.append(" if ($3 == null)");
buffer.append(" {");
buffer.append(" paragraph = ");
buffer.append(" new java.text.AttributedString(");
buffer.append(" \" \",");
buffer.append(" new java.text.AttributedString(");
buffer.append(" $1, ");
buffer.append(" $2, ");
buffer.append(" $2 + 1");
buffer.append(" ).getIterator().getAttributes()");
buffer.append(" ).getIterator();");
buffer.append(" }");
buffer.append(" else");
buffer.append(" {");
buffer.append(" paragraph = ");
buffer.append(" new java.text.AttributedString(");
buffer.append(" $1, ");
buffer.append(" $2, ");
buffer.append(" $2 + $3.length()");
buffer.append(" ).getIterator();");
buffer.append(" }");
buffer.append("");
buffer.append(" java.awt.font.LineBreakMeasurer lineMeasurer = new java.awt.font.LineBreakMeasurer(paragraph, net.sf.jasperreports.engine.export.TextRenderer.LINE_BREAK_FONT_RENDER_CONTEXT);");
buffer.append(" ");
buffer.append(" while (lineMeasurer.getPosition() < paragraph.getEndIndex() && !isMaxHeightReached)");
buffer.append(" {");
buffer.append(" ");
buffer.append(" int startIndex = lineMeasurer.getPosition();");
buffer.append(" ");
buffer.append("");
buffer.append(" java.awt.font.TextLayout layout = lineMeasurer.nextLayout(formatWidth);");
buffer.append("");
buffer.append(" if (isMinimizePrinterJobSize)");
buffer.append(" {");
buffer.append(" ");
buffer.append(" java.text.AttributedString tmpText = ");
buffer.append(" new java.text.AttributedString(");
buffer.append(" paragraph, ");
buffer.append(" startIndex, ");
buffer.append(" startIndex + layout.getCharacterCount()");
buffer.append(" );");
buffer.append(" layout = new java.awt.font.TextLayout(tmpText.getIterator(), grx.getFontRenderContext());");
buffer.append(" ");
buffer.append(" }");
buffer.append("");
buffer.append(" float lineHeight = lineSpacingFactor * ");
buffer.append(" maxFontSizeFinder.findMaxFontSize(");
buffer.append(" new java.text.AttributedString(");
buffer.append(" paragraph, ");
buffer.append(" startIndex, ");
buffer.append(" startIndex + layout.getCharacterCount()");
buffer.append(" ).getIterator(),");
buffer.append(" fontSize");
buffer.append(" );");
buffer.append("");
buffer.append(" if (drawPosY + lineHeight <= textHeight)");
buffer.append(" {");
buffer.append(" drawPosY += lineHeight;");
buffer.append(" ");
buffer.append(" switch (horizontalAlignment)");
buffer.append(" {");
buffer.append(" case 4 :");
buffer.append(" {");
buffer.append(" if (layout.isLeftToRight())");
buffer.append(" {");
buffer.append(" drawPosX = 0;");
buffer.append(" }");
buffer.append(" else");
buffer.append(" {");
buffer.append(" drawPosX = formatWidth - layout.getAdvance();");
buffer.append(" }");
buffer.append(" if (lineMeasurer.getPosition() < paragraph.getEndIndex())");
buffer.append(" {");
buffer.append(" layout = layout.getJustifiedLayout(formatWidth);");
buffer.append(" }");
buffer.append("");
buffer.append(" break;");
buffer.append(" }");
buffer.append(" case 3 :");
buffer.append(" {");
buffer.append(" drawPosX = formatWidth - layout.getAdvance();");
buffer.append(" break;");
buffer.append(" }");
buffer.append(" case 2 :");
buffer.append(" {");
buffer.append(" drawPosX = (formatWidth - layout.getAdvance()) / 2;");
buffer.append(" break;");
buffer.append(" }");
buffer.append(" case 1 :;");
buffer.append(" default :");
buffer.append(" {");
buffer.append(" drawPosX = 0;");
buffer.append(" }");
buffer.append(" }");
buffer.append("");
buffer.append(" draw(layout);");
buffer.append(" }");
buffer.append(" else");
buffer.append(" {");
buffer.append(" isMaxHeightReached = true;");
buffer.append(" }");
buffer.append(" }");
buffer.append(" }");

method
.setBody(new String(buffer));
cc.toClass();
}
}

: