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

外字への道 その4 2008/03/19


前回のコードが問題ありありだっあので、いろいろ修正。
1.文字列の処理が根本的にだめだった。
2.Java Web Start上でjavassistを使えるためのコードに修正。

javassistのバージョンは、CtMethodのメソッドのtoClassでProtectionDomainを引数にとれることができるものを使用しています。

どういうことをしているかといいますと、net.sf.jasperreports.engine.export.TextRendererクラスがロードされるまえに、TextRendererのrenderParagraphメソッドの振る舞いを変更するようにしてJasperReportsで外字を表示できるようにします。

このコードはJasperReportsだったら何でもよいというコードではなくて、JRGraphics2DExporterを使う場合で、さらにWindowsならば有効というコードです。
表示する文字が外字領域のものであれば、その文字を表示するための情報にフォントがEUDCを設定しています。


public class TestJasperUtils {

{

try {

javassist.ClassPool pool = javassist.ClassPool.getDefault();
javassist.CtClass myCtClass;
pool.appendClassPath(new javassist.LoaderClassPath(
TestJasperUtils.class.getClassLoader()));

myCtClass = pool
.get("net.sf.jasperreports.engine.export.TextRenderer");
javassist.CtMethod ctMethod = myCtClass
.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(" java.text.AttributedCharacterIterator iter = tmpText.getIterator();");
buffer.append(" for(char c = iter.first(); c != java.text.CharacterIterator.DONE; c = iter.next()) {");
buffer.append(" java.util.Map map = new java.util.LinkedHashMap();");
buffer.append(" ");
buffer.append(" if(0xe000 <= c && c <= 0xf8ff){");
buffer.append(" int pos = iter.getIndex();");
buffer.append(" map.put(java.awt.font.TextAttribute.FAMILY, \"EUDC\");");
buffer.append(" tmpText.addAttributes(map, pos, pos+1);");
buffer.append(" continue;");
buffer.append(" } ");
buffer.append(" }");
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(" }");


ctMethod.setBody(new String(buffer));

myCtClass.toClass(TestJasperUtils.class.getClassLoader(),
TestJasperUtils.class.getProtectionDomain());

} catch (Exception e) {
e.printStackTrace();
} catch (Error e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
}

}

署名済みのJarを使ったJava Web Start上でJavassistを使うとき 2008/03/11
2008/03/11

普通にEclipse上で開発していて、Javassistでバイトコードをさわって、toClassして問題なかったのにそれをJavaWebStart化するとエラーになりました。

javassist.CannotCompileException: by java.lang.SecurityException: class "クラス名"'s signer information does not match signer information of other classes in the same package


で、うーんと悩んだわけです。

すると、下記のようなものをみつけました。


webstartで、使えるようなので、調べた結果。

toClassするときに、クラスローダーと、ProtectionDomainを渡せばいいようです。

myCtClass.toClass(クラス.class.getClassLoader(), クラス.class.getProtectionDomain());


あと、webstartだと、すぐにクラスがみつかりません。
poolにクラスへのパスを渡してあげます。そのときに、LoaderClassPathを使うとうまくいきました。

pool.appendClassPath(new javassist.LoaderClassPath(クラス.class.getClassLoader()));


ちなみに、webstart上でのリソースをみつける方法に注意!!

Java Web Start でアプリケーションリソースを見つけるには、アプリケーションをロードしたクラスローダを使用します。たとえば、アプリケーションのメインスレッド内で次のメソッドを呼び出します。

this.getClass().getClassLoader();

次のメソッドも使用できます。

Thread.getCurrent().getContextClassLoader();
Java Web Start - よくある質問 (FAQ)


参考

外字への道 その3 2008/02/21
2008/03/19

このコードはだめです。20080319
新しいサンプルコードは、プログラマメモ2: 外字への道 その4

javassistを使ってます。
一字一字調べて外字であれば、フォントファミリをEUDCにしています。
うまくいっているように思えます。

static {

ClassPool pool = ClassPool.getDefault();

CtClass myCtClass;
try {
myCtClass = pool.get("net.sf.jasperreports.engine.export.TextRenderer");
CtMethod ctMethod = myCtClass.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(" char[] cs = $3==null?new char[0]:$3.toCharArray();");
buffer.append(" for(int i=0;i<cs.length;i++){");
buffer.append(" java.util.Map map = new java.util.LinkedHashMap();");
buffer.append(" ");
buffer.append(" if(0xe000 <= cs[i] && cs[i] <= 0xf8ff){");
buffer.append(" map.put(java.awt.font.TextAttribute.FAMILY, \"EUDC\");");
buffer.append(" tmpText.addAttributes(map, i, i+1);");
buffer.append(" continue;");
buffer.append(" }");
buffer.append(" ");
buffer.append(" tmpText.addAttributes(map, i, i+1);");
buffer.append(" }");
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(" }");

ctMethod.setBody(new String(buffer));

myCtClass.toClass();
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
}

}

jasperreports 外字への道 その2 アイデア実証レベル 2008/02/21

この記事での、java.awt.font.TextAttribute.FAMILYの使い方まちがっていました!!!!!

JasperReportsです。
windowsのみでの話しです。

JasperReportsをつかってPDFを出力とかではなくて、JasperReportsを付属のviewerに表示するときとかの話です。

外字(EUDC)を表示するための実証コードです。

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

どういうふうにやっているかといいますと、net.sf.jasperreports.engine.export.TextRendererのrenderParagraphメソッドで処理に手を加えることで、外字混在で表示させています。

メソッドの中身を下記のようにします。※変更箇所は色かえてます。

{
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()
);
java.util.Map map = new java.util.LinkedHashMap();
map.put(java.awt.font.TextAttribute.FAMILY, "MS ゴシック,EUDC");
tmpText.addAttributes(map, 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;
}
}
}


つぎにこのコードをJasperReportsのソースを変更せずにロジックだけ変更します。
ここでJavassistを使います。Javassistを使うと、バイトコードを変更することができます。すごい便利です。

大本のライブラリを、コンパイルしなおす必要がない、もしくはしたくないときとかに重宝します。


下記のコードをJasperReportsを使用するクラスとかに貼ります。staticなブロックにしています。
static {
ClassPool pool = ClassPool.getDefault();

CtClass myCtClass;
try {
myCtClass = pool.get("ja.TextRenderer");
CtMethod ctMethod = myCtClass.getDeclaredMethod("renderParagraph");

ctMethod.getMethodInfo2().setAccessFlags(AccessFlag.PUBLIC);

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(" java.util.Map map = new java.util.LinkedHashMap();");
buffer.append(" map.put(java.awt.font.TextAttribute.FAMILY, \"MS ゴシック,EUDC\");");
buffer.append(" tmpText.addAttributes(map, startIndex, startIndex + layout.getCharacterCount());");
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(" }");

ctMethod.setBody(new String(buffer));

myCtClass.toClass();
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
}

}

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();
}
}

[javassist] もしかして制限? switch文のdefault 2008/02/20

javassistです。
バージョンは、3.6.0.GA です。

えーと、switch文でdefaultを使い方によっては制限がありそう。


Exception in thread "main" java.lang.NullPointerException
at javassist.compiler.CodeGen.atSwitchStmnt(CodeGen.java:539)


なんとなくわかったことは、
{ switch (0) {case 1:break;default:break;} }

という書き方はオッケーで、
{ switch (0) {case 1:default:break;} }

はだめっぽい。

caseとdefaultを続けて書くとNG

途中に;に入れるとよい

{ switch (0) {case 1:;default:break;} }

だとオッケーなよう。

以下、確認のためのコード
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

public class CopyOfTestJavassist {

public static void main(String[] args) throws NotFoundException,
CannotCompileException {
a();
}

static void a() throws NotFoundException, CannotCompileException {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("C");
CtMethod method = cc.getDeclaredMethod("m");
StringBuffer buffer = new StringBuffer();
//buffer.append("{ switch (0) {case 1:break;default:break;} }");//OK
//buffer.append("{ switch (0) {case 1:;default:break;} }");//OK
buffer.append("{ switch (0) {case 1:default:break;} }");//NG
method
.setBody(new String(buffer));
cc.toClass();

// Javaでは下記のswitch文は全部OK
switch (0) {case 1:break;default:break;}
switch (0) {case 1:;default:break;}
switch (0) {case 1:default:break;}
}
}

class C{
public void m(){

}
}

javassist メソッドの置き換えは、setBodyで。 2008/02/20


以前、javassistを使っていて、メソッドのふるまいをかえるのにわざわざremoveしてしまってましたが、単純にsetBodyを使って内容をごっそり置き換えたほうがらくですね。

CtClassクラスのremoveMethodを使わずに、CtMethodのsetBodyを使う。

で、setBodyで置き換えるメソッドがもらう引数は、$1,$2,$3....って感じでうけとれます。
それで、staticメソッドでなければ、フィールド変数にはそのまま名前でアクセスできます。
あと、他のクラスにアクセスするためには、パッケージ名まで書かないとだめです。

以下、適当にサンプル。

import a.A;
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();
A a = new A();
a.m(17);
System.out.println(a);
}

static void a() throws NotFoundException, CannotCompileException {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("a.A");
CtMethod method = cc.getDeclaredMethod("m");
method
.setBody("{ b.B b = new b.B(); x= 217 + f; System.out.println(b.xx);}");
cc.toClass();
}
}


package a;

public class A {
private int f = 17;
String s1 = "o_o!";
int x = 100;
int y =300;

public void m(int a){
x += a;
y += a;

}
public String toString(){
return " x:"+x + " y:" + y;
}

}


package b;

public class B {
public int xx= 177;

public int getX(){
return xx;
}
public B() {
}
}


うーん
javassistをプロジェクトに含めておくて、いざというときものすごく役にたつのではないかという予感がふつふつと。

Javassistをまちがって、Javassitと書いたらグーグルの検索結果で上位にでた 2008/01/17
2008/01/20

かなり恥ずかしいですが、仕方がありません。誤字脱字誤解なんでもありの僕なので、見事な綴りまちがいで、グーグルの検索結果の上位になってしまいました。

javassitではなく、正しくはjavassistでした。

javassit

検索
http://www.google.co.jp/search?hl=ja&rls=GGGL%2CGGGL%3A2006-43%2CGGGL%3Aja&q=javassit&btnG=%E6%A4%9C%E7%B4%A2&lr=

javassist static初期化を利用して、クラスの振る舞いを変えてます。 2007/11/22

javassistを使ったサンプルです。

static初期化を利用して、クラスの振る舞いを変えてます。
static { }
が実行されると、その後で対象のクラスをnewしても変更が効いてますね。
クラスローダーは意識してませんが。。。

package t;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

public class Test {

/*
* !!!! static !!!!
*/
static {
ClassPool cp = ClassPool.getDefault();
CtClass cc = null;
try {
cc = cp.get("t.Hello");
CtMethod m = cc.getDeclaredMethod("say");
m.insertBefore("{ System.out.println(\"o_o! Hello.say():\"); }");
// freeze!!
Class c = cc.toClass();

} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
}

}

public static void main(String[] args) throws Exception {
Hello h = new Hello();
h.say();
}

}

class Hello {
public void say() {
System.out.println("Hello");
}
}