2009년 9월 23일 수요일

ActionScript Reflection (Dynamic Instantiation)

package test
{
    public class ExampleClass
    {
        public static function get instance():Class
        {
            return ExampleClass;
        }
       
        public function getMessage():String {
            return "message";
        }
    }
}

우선 동적으로 객체를 생성할 수 있도록 위와 같이 instance라는 함수를 만들어 둔다.

private function getMessage():void {
    var instance:Object =
              flash.utils.getDefinitionByName("test.ExampleClass");

    var exam:ExampleClass = new instance() as ExampleClass;
    Alert.show(exam.getMessage());
}

이렇게 클래스명을 이용해서 객체를 생성해서, 함수를 호출하면 Alert에 "message"라는 문자열이 보이게 된다.

2009년 9월 22일 화요일

Tooltip과 Datatip의 스타일 지정

Application 전체의 폰트 지정은 아래와 같이 하면 매번 별도의 설정이 필요하지 않다.

global {
    fontFamily:"맑은 고딕";
    fontSize: 11;
}


그런데, Tooltip과 Datatip의 글자가 너무 작아서 보기가 힘든데 아무리 찾아도 글자크기를 키우는 방법을 찾기 힘들었다.
아래처럼만 해 주면 Application 전체에서 나타나는 Tooltip과 Datatip의 폰트를 설정할 수 있다.

ToolTip
{
    fontSize: 11;
}

DataTip
{
    fontSize: 11;
}

2009년 9월 18일 금요일

includeInLayout 속성


화면 컴포넌트 중에 일부를 보였다, 안 보였다 하게 제어해야 할 경우가 있다.
이것을 위해서 visible 속성에 true, false를 주면 안 보이긴 하는데 그 자리가 비어 있어서 영 보기가 안 좋다.
이럴 경우에는 includeInLayout 속성에 true, false를 주면 그 컴포넌트가 원래 코드에 포함되지 않은 것처럼 빈자리 없이 보인다.

단, 절대좌표를 가진 컴포넌트는 당연히 visible을 사용할 때와 똑같이 보일 것이고, Grid, HBox, VBox 등의 Layout Componet에 올라가 있을 경우에만 제대로 동작을 한다.
Flex 하다 보면 다들 느끼겠지만 화면크기가 모든 사용자에게 똑같으면 상관없지만 그렇지 않은 관계로 대부분의 화면 컴포넌트들을 HBox, VBox에 올려 놓고 왼쪽정렬이나 오른쪽정렬을 하는 것이 가장 편하다.

2009년 9월 3일 목요일

Java VO를 ActionScript VO로 변환하기

BlazeDS를 사용하려면 동일한 내용을 담는 Vo를 Java 버전과 ActionScript 버전의 짝을 만들어 줘야 한다.
근데 이번에 한꺼번에 대량 작업을 하려다 보니 너무 귀찮아져서 Java VO 디렉토리를 쭉 훑어서 특정 디렉토리 아래에 ActionScript VO로 변환해서 저장해 주는 초간단/초허접 프로그램을 만들어 보았다.
필요하신 분들은 용도에 맞춰 고쳐 쓰시고, 고쳐 달라고 하기는 없기~

package com.my.vo.data;

import com.my.vo.CommonVo;

// Generated 2009. 8. 24 ???? 12:54:51 by Hibernate Tools 3.2.4.GA

/**
 * Foo generated by hbm2java
 */
public class FooVo extends CommonVo{

    /**
     *
     */
    private static final long serialVersionUID = 4526158581216990458L;
   
    public FooIdVo id;
    public String barName;
    public String barType;
    public int barLength;

    ...
}
이것이 원본 Java VO


package vo.data
{
    import vo.CommonVo;
   
    [Bindable]
    [RemoteClass(alias="com.my.vo.data.FooVo")]

    public class FooVo extends CommonVo
    {       
        public var id:FooIdVo;
        public var barName:String;
        public var barDesc:String;
        public var barType:String;
        public var barLength:int;
    }
}
이것이 원하는 형태의 ActionScript VO


package flex;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;

public class ConvertJavaToAS {
    public static final String packagePrefix = "com.my.";

    public static void main(String[] args) throws Exception {
        String javaDir = "C:/workspace/com/my/vo";
        String asDir = "D:/temp/vo";
       
        File inDir = new File(javaDir);
       
        traverse(inDir, asDir);
    }

    public static void traverse(File in, String out) throws Exception {
        File[] files = in.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory())
                traverse(files[i], out);
            else if (files[i].getName().endsWith("Vo.java")){
                convert(files[i], out);
            }
        }
    }
   
    public static void convert(File java, String out) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader(java));
       
        String strPackage = "";
        String strClass = "";
        StringBuilder strVar = new StringBuilder();
       
        String line = null;
        while ( (line = br.readLine()) != null ) {
            if (!strClass.equals("") && line.trim().startsWith("public " + strClass + "("))
                break;
            else if (line.trim().startsWith("package")) {
                String[] strs = line.trim().split(" ");
                strPackage = strs[1].substring(0, strs[1].length()-1);
            }
            else if (line.trim().startsWith("public class")) {
                String[] strs = line.trim().split(" ");
                strClass = strs[2];
            }
            else if (line.trim().startsWith("public")) {
                String[] strs = line.trim().split(" ");
                strVar.append("\t\tpublic var ")
                      .append(strs[2].substring(0, strs[2].length()-1)).append(":")
                      .append(mapDataType(strs[1])).append(";\n");
            }
        }
       
        br.close();
       
        save(strPackage, strClass, strVar.toString(), out);
    }
   
    public static String mapDataType(String javaType) {
        if (javaType.equals("BigDecimal")) {
            return "String";
        }
        else
            return javaType;
    }
   
    public static void save(String pack, String clazz, String vars, String out) throws Exception {
        String subdir = pack.substring(pack.lastIndexOf(".")+1);
        String outDir = out + "/" + subdir;
        File asDir = new File(outDir);
        if (!asDir.exists())
            asDir.mkdirs();
       
        String asPackage = pack.substring(packagePrefix.length());
       
        BufferedWriter bw = new BufferedWriter(new FileWriter(outDir + "/" + clazz + ".as"));
        bw.write("package " + asPackage + "\n{\n");
        bw.write("\timport vo.CommonVo\n\n");
        bw.write("\t[Bindable]\n");
        bw.write("\t[RemoteClass(alias=\"" + pack + "." + clazz + "\")]\n\n");
        bw.write("\tpublic class " + clazz + " extends CommonVo\n\t{\n");
        bw.write(vars);
        bw.write("\t}\n}");
       
        bw.flush();
        bw.close();
    }
}

이것이 변환 프로그램


사용시 수정해야 할 것들은 굵은 글씨도 표시된 부분

1. packagePrefix : Java VO와 ActionScript VO의 패키지 경로를 다르게 사용할 경우 Java VO의 package에서 잘라내야 할 앞부분. 같은 패키지 경로를 사용하면 이걸 빈 문자열로 설정하면 된다.
2. javaDir : Java VO 들이 있는 최상위 디렉토리. 이 아래에 있는 모든 Java VO를 찾아서 ActionScript VO로 변환한다.
3. asDir : 생성된 ActionScript VO들을 저장할 디렉토리. 패키지에 따라 하위디렉토리를 만들어서 저장.
4. Vo.java : 나는 일단 Java VO 파일의 postfix가 Vo.java라서 이렇게 썼고, 필요에 따라서 고쳐 쓰시길.
5. mapDataType : Java의 데이터타입을 AS이 데이터타입으로 매핑시켜 주는 메소드. 원하는만큼 추가해 주면 됨.
6. CommonVo : 나는 공통으로 상속할 상위클래스가 있어서 이렇게 썼고, 필요없으면 빼고 다르면 고쳐서 쓰시길...


띄어 쓰기 같은 건 무조건 한칸으로 가정하고 처리했으니까 Hibernate Tools로 생성한 VO를 사용하거나, 줄 잘 맞춰서 직접 정성껏 만든 VO를 사용해야 원하는 결과를 얻을 수 있다.

2009년 8월 21일 금요일

HQL UNION 제약사항

요즘 Hibernate가 필요해서 이것저것 해 보고 있다.
특히 장점은 한 번 프로그램을 작성해 놓으면 설정만으로 다른 종류의 DBMS에서도 그대로 사용이 가능하다는 거다. 그래서, 열심히 기존에 있던 오라클용 SQL을 HQL로 변경해서 테스트해 보는데 이거 제약이 좀 있다.
참고로, HQL 테스트환경은 Hibernate Tools를 Eclipse에 설치해서 Configuration 잡아 주고, Hibernate perspective에서 Hibernate Configurations 탭을 통해 HQL Editor를 띄우면 일반 DB client처럼 쿼리 결과도 볼 수 있고, HQL로부터 실제로 생성되는 SQL도 볼 수 있다.

일단, 인터넷에서 HQL에서 UNION이 지원되나 찾아 보면 안 된다는 내용이 대세다.
하지만, 내가 오라클을 써서 그런지 내가 쓰는 Hibernate 버전에서 지원되는 건지 모르겠지만 UNION만은 잘 된다. 3개를 묶어도 잘 된다. 그런데, UNION을 IN의 subquery에서 사용하면 못 한다고 에러가 난다.

[code sql] from t1 where col1 in (    select col2 as name1    from t2    where col3 = 'a'    union    select col2 as name1    from t3    where col3 = 'a' ) [/code]
처음엔 에러 메시지가 union 부분이라고 해서 union을 못 쓰는 줄 알았는데, 하다보니 in 안의 subquery에서 사용해서 그런 거였다. 결국 그래서 아래와 같이 바꿨다.

[code sql] from t1 where col1 in (   select col2 as name1   from t2
   where col3 = 'a' ) union from t1 where col1 in (   select col2 as name1   from t3   where col3 = 'a' ) [/code]
덕분에 쿼리가 왕창 길어졌다. ㅡ.ㅡ;
이렇게 쿼리 만드는 일이 거의 없나? 하긴 나는 원래 그냥 UNION도 거의 안 써 봤으니...
암튼 잘 되기만 하면 된다.

2009년 8월 20일 목요일

Flex + BlazeDS + Java 웹프로젝트용 Ant Build.xml

Eclipse + Flex Builder로 개발한 웹애플리케이션을 WAR로 export하면 Flex 소스코드까지 그대로 묶여서 들어간다.
그래서, Flex나 Java 코드는 다시 compile하지 않고 웹애플리케이션으로 deploy할 때 꼭 필요한 컴파일된 파일들과 리소스 파일들을 별도의 디렉토리에 복사해 WAR로 묶은 다음 지정된 위치로 복사해 주는 기능이다.
사실 그냥 WAR로 묶어도 별 문제는 없을 것 같지만 혹시나 소스가 공개될 수도 있고, Flex 소스가 다 들어가면 WAR 파일이 너무 커져서 만들어 봤다.

 <!-- 현재 프로젝트의 컴파일된 코드를 WAR로 묶어 webapp.dir 속성에 지정된 디렉토리에 복사 . 추가로 컴파일은 하지 않음 -->
<project name="MyWebApp" basedir="." default="deploy">

    <!-- Directory to deploy the generated WAR file -->
    <property name="webapp.dir" value="C:/apache-tomcat-5.5.26/webapps" />
   
    <property name="web.app.name" value="FLEXAPP" />
    <property name="package.name" value="${web.app.name}.war" />
    <property name="dest.dir" value="target" />
    
    <property name="flex.file.dir" value="bin-debug"/>
    <property name="web.content.dir" value="WebContent"/>

    <target name="prepare">
        <mkdir dir="${dest.dir}" />
    </target>

    <target name="package" depends="prepare">
      <echo>Packaging...</echo>
     
      <!-- copy Flex and Java Web files -->
      <copy todir="${dest.dir}" overwrite="false">
        <fileset dir="${flex.file.dir}"/>
        <fileset dir="${web.content.dir}"/>
      </copy>
        
      <!-- package war file -->
      <war destfile="${dest.dir}/${package.name}"
              webxml="${dest.dir}/WEB-INF/web.xml"
              basedir="${dest.dir}"/>
    </target>

    <!-- deploy the generated war file -->
    <target name="deploy" depends="package">
      <echo>Deploying...</echo>
      <copy file="${dest.dir}/${package.name}"
              tofile="${webapp.dir}/${package.name}"
              overwrite="true" />
      <delete file="${dest.dir}/${package.name}" />
    </target>

</project>


2009년 7월 1일 수요일

Flex I18N 메시지 컴파일용 Ant Build.xml

플렉스를 하면서 다국어 처리를 하게 되었는데 할 때마다 명령 프롬프트에서 스크립트 실행하기도 귀찮고 함께 JSP도 사용하는데 따로 관리하려니 너무 머리가 아파서, 하나의 properties를 이용해서 mxmlc를 이용해 플렉스용 메시지파일을 생성하고, JSP용 properties 파일까지 만들어 주는 Ant 빌드파일을 만들었다. 다국어 메시지 파일을 한 번 만들고 끝날 게 아니라면 유용하게 사용할 수 있다.


기능 :
현재 다국어 메시지 파일을 Flex에서 사용할 수 있도록 swf로 컴파일하고 bin.debug.dir 속성의 디렉토리에 복사한 후에, 추가로 JSP에서 사용할 수 있도록 변환하고 java.locale.dir 속성의 디렉토리에 복사

사용법 :
나는 이클립스에서 사용하는데, 플렉스 프로젝트 루트디렉토리에 이 내용으로 build.xml을 만들고, 마우스 오른쪽 클릭하고 "Run As > Ant Build" 를 선택하면 실행된다.

-- 기본적인 다국어 컴파일에 관한 내용은 '예제로 배우는 플렉스'를 참고하시길.

<project name="LocaleCompile" basedir="." default="ko_kr">

    <property name="resource.dir" value="flex_src/resource" />
    <property name="bin.debug.dir" value="bin-debug/resource" />
    <property name="java.locale.dir" value="src/locale" />
  
    <property name="en.us.locale" value="en_US" />
    <property name="en.us.file" value="${en.us.locale}_ResourceModule.swf" />
    <property name="ko.kr.locale" value="ko_KR" />
    <property name="ko.kr.file" value="${ko.kr.locale}_ResourceModule.swf" />
  
    <property name="resource.bundles" value="collections,containers,controls,core,effects,formatters,flexsong,logging,SharedResources,skins,states,styles,utils,validators" />
  
    <!-- english -->
    <target name="en_us">
        <!-- flex -->
        <exec dir="${resource.dir}" executable="mxmlc">
            <arg line="-locale=${en.us.locale} -source-path=locale/{locale} -include-resource-bundles=${resource.bundles} -output ${en.us.file}"/>
        </exec>
        <copy file="${resource.dir}/${en.us.file}" tofile="${bin.debug.dir}/${en.us.file}"/>
      
        <!-- java -->
        <exec dir="${resource.dir}/locale/${en.us.locale}" executable="native2ascii">
            <arg line="-encoding UTF-8 flexsong.properties ${basedir}/${java.locale.dir}/flexsong_${en.us.locale}.properties"/>
        </exec>
    </target>
  
    <!-- korean -->
    <target name="ko_kr" depends="en_us">
        <!-- flex -->
        <exec dir="${resource.dir}" executable="mxmlc">
          <arg line="-locale=${ko.kr.locale} -source-path=locale/{locale} -include-resource-bundles=${resource.bundles} -output ${ko.kr.file}"/>
        </exec>
        <copy file="${resource.dir}/${ko.kr.file}" tofile="${bin.debug.dir}/${ko.kr.file}"/>
      
        <!-- java (JSP의 charset이 UTF-8이므로 encoding을 동일하게 처리하기 위해 encoding 옵션에 UTF-8을 추가했다) -->
        <exec dir="${resource.dir}/locale/${ko.kr.locale}" executable="native2ascii">
          <arg line="-encoding UTF-8 flexsong.properties ${basedir}/${java.locale.dir}/flexsong_${ko.kr.locale}.properties"/>
        </exec>
    </target>

</project>