sexta-feira, 23 de julho de 2010

Conversão Semi-automática de Classes Java para AS

Esses dias eu estava brincando com uma aplicação e me entendiou o fato de ter que ficar duplicando as classes VO do Java para o Flex na mão. Comecei a pesquisar e vi a solução GAS do Granite. Contudo achei a geração das classes em ActionScript muito verbosa. Eu queria algo mais simples, que o nome da classe, o alias, e os atributos fossem copiados. Acabei não encontrando algo simples assim, aí acabei fazendo uma solução caseira rapidamente usando o próprio Flex. Não me importei com testes unitários, mas o fato é que a aplicação funcionou bem para as classes que eu estava fazendo. Para usar, basta colar o código da classe Java no Browser. Estou postando também o código fonte, apesar  de ele ser bem porquinho J, para quem kiser adaptar a solução para seus propósitos.

public class J2ASConverter {
         public function convertToAS(javaClass:String):String {
              var str:String='[RemoteClass(alias="';
              str+=buildAlias(javaClass)+'")]';
              str+="\npublic class "+getClassName(javaClass)+"{";
              str+=buildFields(javaClass);
              return str+"\n}";
         }

         private function buildFields(javaClass:String):String {
              var fieldsAS:String="";
              for each(var field:Object in getFields(javaClass)) {
                   fieldsAS+="\n\tpublic var "+field.fieldName+":"+field.fieldType+";";
              }
              return fieldsAS;
         }

         private function getFields(javaClass:String):Array {
              var classSearchIndex:int=javaClass.indexOf("class");
              if(classSearchIndex>=0) {
                   do {
                        ++classSearchIndex;
                   } while(classSearchIndex"{");
                   ++classSearchIndex;
                   var fields:Array=[];
                   var abreChave:int=0;
                   while(classSearchIndex
                        if(abreChave==0) {
                            var line:String="";
                             while(classSearchIndex";") {
                                 line+=javaClass.charAt(classSearchIndex);
                                 ++classSearchIndex;
                            }
                            line=removePossibleJavaAnnotations(line);
                            if(isField(line)) {
                                 if(line.indexOf("}")==-1) {
                                      fields.push(extractTypeAndName(line));
                                 }
                            } else {
                                 ++abreChave;
                            }
                        } else if(javaClass.charAt(classSearchIndex)=="}") {
                            --abreChave;
                        } else if(javaClass.charAt(classSearchIndex)=="{") {
                            ++abreChave;
                        }
                        ++classSearchIndex;
                   }
                   return fields;
              }
              return [];
         }

         private function removePossibleJavaAnnotations(line:String):String {
              var searchIndex:int=line.indexOf("@");
              if(searchIndex!=-1) {
                   ++searchIndex;
                   while(searchIndex"(") {
                        ++searchIndex;
                   }
                   var parentesiNumber:int=1;
                   ++searchIndex;
                   while(searchIndex
                        if(line.charAt(searchIndex)=="(") {
                            ++parentesiNumber;
                        } else if(line.charAt(searchIndex)==")") {
                            --parentesiNumber;
                        }
                        ++searchIndex;
                   }
                   if(searchIndex
                        return line.substring(++searchIndex);
              }
              return line;
         }

         private function extractTypeAndName(line:String):Object {
              line=line.split("=")[0];
              var searchIndex:int=line.length-1;
              while(searchIndex!=-1&&line.charAt(searchIndex)==" ") {
                   --searchIndex;
              }
              var javaFieldName:String="";
              while(searchIndex!=-1&&line.charAt(searchIndex)!=" ") {
                   javaFieldName=line.charAt(searchIndex)+javaFieldName;
                   --searchIndex;
              }
              while(searchIndex!=-1&&line.charAt(searchIndex)==" ") {
                   --searchIndex;
              }
              var javaFieldType:String="";
              while(searchIndex!=-1&&line.charAt(searchIndex)!=" ") {
                   javaFieldType=line.charAt(searchIndex)+javaFieldType;
                   --searchIndex;
              }
              return { fieldType:java2ASType(javaFieldType),fieldName:javaFieldName };
         }

         private function java2ASType(javaType:String):String {
              javaType=javaType.replace("<.*>","");
              javaType=javaType.replace(" ","");
              if(javaType=="Set"||javaType=="List") {
                   return "ArrayCollection";
              } else if(javaType=="int"||javaType=="Integer"||javaType=="short"||javaType=="Short"||javaType=="long"||javaType=="Long"||javaType=="float"||javaType=="Float"||javaType=="double"||javaType=="Double") {
                   return "Number";
              } else if(javaType=="Calendar") {
return "Date";
}
              return javaType;
         }

         private function isField(line:String):Boolean {
              var firstLinePart:String=line.split("=")[0];
              return firstLinePart.indexOf("{")==-1;
         }

         private function buildAlias(javaClass:String):String {
              var packageIndex:int=javaClass.indexOf("package");
              var packageNameBegin:int=0;
              var alias:String="";
              if(packageIndex>=0) {
                   for(packageNameBegin=packageIndex+7;javaClass.charAt(packageNameBegin)==" ";++packageNameBegin) {
                   }
                   if(javaClass.indexOf(";")>packageNameBegin) {
                        var char:String=javaClass.charAt(packageNameBegin);
                        while(char!=";") {
                            alias+=char;
                            char=javaClass.charAt(++packageNameBegin);
                        }
                        alias+="."+getClassName(javaClass);
                   }
              }
              return alias;
         }

         private function getClassName(javaClass:String):String {
              var classWordIndex:int=javaClass.indexOf("class");
              if(classWordIndex>=0) {
                   classWordIndex+=5;
                   while(javaClass.charAt(classWordIndex)==" ") {
                        ++classWordIndex;
                   }
                   var className:String="";
                   do {
                        className+=javaClass.charAt(classWordIndex);
                        ++classWordIndex;
                   } while(classWordIndex" "&&javaClass.charAt(classWordIndex)!="{");
                   return className;
              }
              return "";
         }
     }

Se a classe completa do Java for utilizada, incluindo a linha com o package, a mini-aplicação monta o metadado RemoteClass com o respectivo alias.
Espero que a aplicação ajude mais alguém.

4 comentários:

LuizRolim disse...

Muito útil, especialemente para projetos novos.

Parabéns pela iniciativa.

Bruno Picinin disse...

Muito bom!
Pra mim funcionou perfeitamente. Só fica a dica de converter Calendar pra Date no flex.

Renzo disse...

Fix do Calendar para Date feito. Obrigado pela dica

Felipe disse...

Ótimo Post!!!!

Muuuito útil!!!