퇴근5분전

 

 

 이번 프로젝트에서 ExtJs 디자이너로 화면을 만들어 놓은것을

Ext.Net에 맞춰 변환 하는 프로그램을 만들면서 살짝 이력을 남겨본다.

 

# 정보수집..

1. 자바스크립 Object 구성으로 만들어진다. composite!

2. 그렇다면 트리 구조로 데이타를 추적할수 있다.

3. 문자열 > Object 변환이 필요하다. json으로 되는지 보자.

4. 각 프로퍼티에 대응하는 object 프로퍼티 맵을 어떻게 구성해야 하는지.. 고민해봐야 한다.

5. 기간! 주어진 시간이 짧기에 [ 변환 소스 만드는 시간 = 손으로 바꿔야 하는 시간 ] 이 쌤쌤이어야 한다.

 

! 정보수집 된 내용을 기반으로 머리속에서 프로그램이 동작하는 부분들을 미리 이미지화 한다.

 

# 제작..

   쿵짝 쿵짝.. 만드는데 3시간, 테스트 3시간!!

# 결과

   OK! 소스 변환까지는 금방 완성이 되었는데... 다른 일들이 껴들어와서 누적시간이 더 걸렸음.

         테스트 할때는 실제 변환해서 화면에 뿌려보니 디자이너로 제작한 내용과 다른 부분들이 존재하여

         추적하여 디버깅하였음.

         리플렉션 + 대리자를 이용! 소스가 엄청 짧고 간단한다.

 

# Hint!

  소스 라인은 833 라인!으로 ExtJs의 필요한 부분들을 최대한 필요한 만큼 수용한 변환 툴 완성!

 

 - 사용된 디자인 패턴 : Visitor [ 역시 타입별로 대응해서 뭔가 처리 하고자 할때 ... 좋음. ]

 

# 변환기 호출 처리 로직

1.

string                                                    // 변환된 문자열

ToConvert (

            EXTNETBASE ex,                         // 변환 대상

            int depth,                                            // 현재 깊이

               Func<EXTNETBASE, int, string> Fn // 변환기

2.

Func<EXTNETBASE, int, string>                     // 타입에 맞는 변환메서드

GetConvert(

           string xType                                // 타입

)

3.

string                                                   // 타입에 맞는 변환 결과

ToContainer(

           EXTNETBASE ex,                         // 변환 대상

           int depth                                    // 깊이

)

 

기본 흐름은 위처럼 변환대상에 대해 시작하면(ToConvert) 해당 타입을 변환 할 수 있는 메서드를 꺼내서(GetConvert)

변환(ToContainer)

 

 

    public interface IVisitor
    {
        string Visit(IAcceptor act);
    }
    public interface IAcceptor
    {
        EXTNETBASE GetInstance();
        string Acceptor(IVisitor visit);
    }

 

  이번에 개발하면서 대리자+ 리플렉션 이용해서 소스를 줄이고 시간을 벌었다. 

 

// extjs 의 문자열들을 object 트리 구조로 변환 시켜줌 ( Json이용 )

EXTNETBASE m = JsonConvert.DeserializeObject<EXTNETBASE>("    extjs design source  ");

 

// 트리구조의 object를 비지터로 모두 순회 하면서 각 타입별로 변환 시킴.

ExtNetConverter vis = new ExtNetConverter();
string s = m.Acceptor(vis);

 

// Object

public class EXTNETBASE : IAcceptor
{

        public Collection<EXTNETBASE> items { get; set; }

        public string Id { get; set; }
        public string name { get; set; }
        public string xtype { get; set; }

   
        . . .

          
        public string Acceptor(IVisitor visit)
        {
            return visit.Visit(this);
        }

        public EXTNETBASE GetInstance()
        {
            return this;
        }
}

 

public class ExtNetConverter : IVisitor
{
        public string Visit(IAcceptor act)
        {
            EXTNETBASE cont = act.GetInstance();
            return ToConvert(cont, 0, GetConvert(cont.xtype));
        }

       

        // 탭의 깊이만큼 생성 반환!

        static Func<int, string> Tab = (depth) => { return new string('\t', depth); };

 

        // 구조의 깊이를 추적할수 있는 대리자.

        static Func<EXTNETBASE, string, int, int> CalcMaxDepth = (ext, collectionPropertyName, depth) => {

            // todo : 트리구조이므로 깊이를 추적함.

            PropertyInfo collectionProperty = ext.GetType().GetProperty(collectionPropertyName, BindingFlags.Public | BindingFlags.Instance);
            if (collectionProperty != null)
            {
                Collection<EXTNETBASE> coll = collectionProperty.GetValue(ext, null) as Collection<EXTNETBASE>;
                if (coll != null)
                {
                    int maxChildDepth = 0;
                    int temp = 0;
                    foreach (var item in coll)
                    {
                        temp += CalcMaxDepth(item, collectionPropertyName, depth);
                        if (temp > maxChildDepth) temp = maxChildDepth;
                    }
                    depth += maxChildDepth;
                }
            }
            return depth;
        };

 

       string ToConvert(EXTNETBASE ex, int depth, Func<EXTNETBASE, int, string> Fn) {

 

            // todo : 객체별 변환메서드를 호출하며, 탭으로 서식 유지 시켜 보기 편하게 만듬.
            string result = Tab(depth) + (Fn != null ? Fn(ex, depth) : "@*## Convert미지원 " + ex.xtype + " ##*@");
            if (ex.items != null && ex.items.Count > 0)
            {
                result += ".Items(" + Environment.NewLine + string.Join(","+Environment.NewLine, ex.items.Select(s =>

                                           ToConvert(s, depth+1, GetConvert(s.xtype))).ToArray());
                int itemDepth = CalcMaxDepth(ex, "items", depth);
                result += Environment.NewLine + Environment.NewLine + Tab( itemDepth ) + ")";
            }

            if (ex.dockedItems != null && ex.dockedItems.Count > 0)
            {
                result += ".DockedItems(" + Environment.NewLine + string.Join("," + Environment.NewLine, ex.dockedItems.Select(s =>

                                          ToConvert(s, depth+1, GetConvert(s.xtype))).ToArray());
                int dockedItemsDepth = CalcMaxDepth(ex, "dockedItems", depth);
                result += Environment.NewLine + Environment.NewLine + Tab(dockedItemsDepth) + ")";
            }

            if (ex.gridcolumns != null && ex.gridcolumns.Count > 0)
            {
                result += ".ColumnModel(" + Environment.NewLine + string.Join("," + Environment.NewLine, ex.gridcolumns.Select(s =>

                                          ToConvert(s, depth+1, GetConvert(s.xtype))).ToArray());
                int columnModelDepth = CalcMaxDepth(ex, "gridcolumns", depth );
                result += Environment.NewLine + Environment.NewLine + Tab(columnModelDepth) + ")";
            }
            return result;
        }

 

        Func<EXTNETBASE, int, string> GetConvert(string xType)
        {

            // todo : 각 타입별 변환 메서드 반환

            switch (("" + xType).ToLower())
            {
                default:
                    return new Func<EXTNETBASE, int, string>((e, i) => { return @"@*"+xType+"*@"; });
                case "container":
                    return ToContainer;

                case "textfield":
                    return ToTextfield;

                case "textareafield":
                    return ToTextareafield;

              

                ... 각 타입별 변환 메서드!

           }

       }

 

        string ToContainer(EXTNETBASE ex, int depth)
        {
            string content = "X.Container()";
            content += GetCommonConvert(ex);
            return content;
        }

 

        string GetCommonConvert(EXTNETBASE ex, string opt = "")
        {

               // todo : 객체들 공통프로퍼티 변환목록을 묶어서 진행.

         }

 

         private static string GetPropertyConvert(EXTNETBASE ex, string propertyName, string opt = "")
        {

                // todo : Js변환 대상 프로퍼티 -> Net 변환

        }
        // 이후로는

        // 20여개의 변환 메서드

        // 15개 정도의 프로퍼티 변환처리.

}