domingo, 7 de junho de 2015

parsing json date elements with angular-js automatically

problem: parsing a json string to a javascript object convert date to string and not to a Date object

solution: introspect the object after the transformation replacing string objects with date objects.

How to do it?

The first thing is to define your app protocol and how date will be formated as a string object.  Be consistent in all functions of your app.

A good choice is accomplish ISO 8601 standard. This assumes that dates is represented always in this format YYYY-MM-DD, To express Times as string objects use this mask HH:mm:ss and Timestamp you can set the mask for YYYY-MM-DDTHH:mm:ss.sssZ. Real time application will requires more precision, anyway, this is not the focus here.

@client side


The app at client side must guarantee that every http response will call the funcion paseDate below:

 var ISO8601_DATE_TIME_FORMAT = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,})(Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;  
 function parseDate(obj) {  
      if (!obj || obj == 'undefined') return;  
      for (var prop in obj) {  
           var t_name = typeof(obj[prop]);  
           if (t_name == "object") {  
                parseDate(obj[prop]);  
           } else if (t_name == "string") {  
                var search;  
                var valor = obj[prop];
                if (search = valor.match(ISO8601_DATE_TIME_FORMAT)) {  
                     try {  
                          obj[prop] = new Date(Date.parse(valor));  
                     } catch (e) {};  
                }  
           }  
      }       
 }  
 function parseJSON(s) {  
      var o = JSON.parse(jsonString);  
      parseDate(o);  
 }  

Another good choice is include moment.js in your project

Example:


      var obj = parseJSON(s)  

Where 's' is a string from a http response in json format,
like the one below

           {"id":"dL93if5p47Mad3_36mXzYf","idTarefa":"dOPxTsNKkmO9AsfTaEvrLG","idEmpresa":"2sR0qM_t4ROaDL1Zj7Kvns","idUsuario":"6br-0sd1AyrbEmo4Hh39-J","nomeUsuario":"RiCARDO A. HARARI","dataCadastro":"2015-04-20T05:26:09.009"}}

obj.dataCadastro will be replaced by a Date object and then you can put it into app scope and associate with an input type  "date" html5 component.

INPUT TYPE="date" class="form-control" ng-model="obj.dataCadastro"

With angular-js each response can be automatically parsed

Sample config that will trigger paseDate

 myAppReferenceObject.config(["$httpProvider", function ($httpProvider) {  
      $httpProvider.defaults.transformResponse.push(function(responseData){  
           parseDate(responseData);  
           return responseData;  
      });  
 }]);  


myAppReferenceObject is your app, the angular.module,
All http data response will be automatically transformed to a date.


 $http.post(myURL)  
      .success(function(data) {  
           console.log(data.dataCadastro); // here dataCadastro is already a date object  
      ...  


@ server side

In the java server side you have to implement the jsonification process
I recommend gson lib to jsonification strings from/to a plain value object (aka pojo)
https://code.google.com/p/google-gson/

For GSON just define a serializer and a deserialize class as follows:


 package com.technique.engine.data.nosql;  
 import java.lang.reflect.Type;  
 import java.text.SimpleDateFormat;  
 import java.util.Date;  
 import com.google.gson.JsonDeserializationContext;  
 import com.google.gson.JsonDeserializer;  
 import com.google.gson.JsonElement;  
 import com.google.gson.JsonParseException;  

 public class DateDeserealizer implements JsonDeserializer<Date> {  
      /**  
       * returns a date object of the json element  
       * json element can start with mask: yyyy-MM-dd or dd/MM/yyyy   
       * and finish with HH:mm:ss or HH:mm:ss.sss or HH:mm:ss.sssz  
       * valid json formats sample: 01/12/2014 ; 2014-12-01 ; 01/12/2014T23:02:01; 01/12/2014T23:02:01.987; 01/12/2014T23:02:01.987Z; 2014-12-01T23:02:01.987   
       */  
      @SuppressWarnings("unused")  
      @Override  
      public Date deserialize(JsonElement json, Type arg1, JsonDeserializationContext arg2) throws JsonParseException {  
           if (json == null) return null;  
           String s = json.getAsString();  
           try {  
                if (s.indexOf('T') > -1) {  
                     int i = s.indexOf('Z');  
                     // date with / separator will will assume this mask dd/MM/yyyy - and date with - separator will be expected yyyy-MM-dd  
                     boolean ddmmyyyy = s.indexOf('/') > -1;  
                     if (i > -1) s = s.substring(0, i);  
                     i = s.length();  
                     SimpleDateFormat sdf = null;  
                     if (i == 10) {  
                          sdf = new SimpleDateFormat(ddmmyyyy ? "dd/MM/yyyy" : "yyyy-MM-dd");  
                     } else if (i == 17) {  
                          sdf = new SimpleDateFormat(ddmmyyyy ? "dd/MM/yyyy'T'HH:mm:ss" : "yyyy-MM-dd'T'HH:mm:ss");  
                     } else if (i == 21) {  
                          sdf = new SimpleDateFormat(ddmmyyyy ? "dd/MM/yyyy'T'HH:mm:ss.sss" : "yyyy-MM-dd'T'HH:mm:ss.sss");  
                     } else if (i == 8) {  
                          sdf = new SimpleDateFormat(ddmmyyyy ? "dd/MM/yy" : "yy-MM-dd");  
                     }  
                     if (sdf != null) return sdf.parse(s);  
                }  
           } catch (Exception e) {  
           }  
           try {  
                return new Date(json.getAsLong());  
           } catch (Exception e2) {  
                throw new JsonParseException("invalid date");  
           }  
      }  
 }  





 package com.technique.engine.data.nosql;  
 import java.lang.reflect.Type;  
 import java.text.SimpleDateFormat;  
 import java.util.Date;  
 import com.google.gson.JsonElement;  
 import com.google.gson.JsonPrimitive;  
 import com.google.gson.JsonSerializationContext;  
 import com.google.gson.JsonSerializer;  
 public class DateSerializer implements JsonSerializer<Date> {  
      @SuppressWarnings("unused")  
      @Override  
      public JsonElement serialize(Date src, Type arg1, JsonSerializationContext arg2) {  
           return src == null ? null : new JsonPrimitive(getJsonDate(src));  
      }  
      protected static String getJsonDate(Date date) {  
           SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'");  
           return sdf1.format(date);
      }  
 }  


Use the method below to get the Gson object:

      private static Gson getGson() {  
           DateSerializer ser = new DateSerializer();  
           DateDeserealizer deser = new DateDeserealizer();  
           return new GsonBuilder()  
             .registerTypeAdapter(Date.class, ser)  
             .registerTypeAdapter(Date.class, deser)  
             .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.VOLATILE)  
             .create();  
      }  



Sample:

      String fromHTTPRequest = "{\"maleFemale\":\"1\",\"name\":\"John\",\"email\":\"john@a.com\"}";  
      UsuarioVO u = getGson().fromJson(fromHTTPRequest, UsuarioVO.class)  
      System.out.println(u.name); // print 'John'  


UsuarioVO is a pojo with at least these 3 public atributes:

public int maleFemale; //0-undefined, 1-male, 2-female
public String name;
public String email;



sábado, 22 de junho de 2013

Google GWT uma excelente decisão de projeto.

Após algumas provas de conceito para ajudar a escolher qual plataforma client usar em aplicativos web, limitado a plataforma java, a melhor decisão ponderando todos os prós e contras é o toolkit da google, o GWT. O maior desafio esta em encontrar pessoas qualificadas mas a maior vantagem é o rapido aprendizado, anulando o problema. Plugins para o IDE (eclipse, ...) e possibilidade de criar e depurar interfaces visualmente são alguns dos facilitadores para o rápido aprendizado. Outro fator relevante e´o google como patrocinador e este usar para o seu ganha pão principal, a interface do adwords. No desenho da solução você utilizara a buferização e processamento no cliente sem se preocupar com a plataforma da ponta do cliente. Pode usar desde o recomendado MVP ou outras arquiteturas e patterns. Isso resulta em mais tempo para resolver os problemas de negócios minimizando os custos da equipe. A capacidade de processamento da interface (gui) é surpreendente e por isso pode usar para programar games. Custom tags, jsf, html5 e outras soluçoes js são usadas e tem um imenso legado mas o gwt é a melhor decisão.

sábado, 2 de fevereiro de 2013

frase do dia

quanto mais estudo mais ciente fico da minha insignificancia.

domingo, 26 de agosto de 2012

DhtmlGoodies - getNodeOrders method

DhtmlGoodies has a nice drag n´drop treeview component.
If you change 2 lines in method getNodeOrders (inside the script JSDragDropTree) you will be able to set up a char ID to the tree nodes. This was the only issue I encountered.

Edit the drag-drop-folder-tree.js script

inside the function getNodeOrders
,getNodeOrders : function(initObj,saveString)
...

replace this line:
var numericID = li.id.replace(/[^0-9]/gi,'');
with
var numericID = li.id;

and replace this
var numericParentID = li.parentNode.parentNode.id.replace(/[^0-9]/gi,'');
with this
var numericParentID = li.parentNode.parentNode.id;




Thats it! Now JSDrapDropTree will work with char nodes IDs.





DhtmlGoodies - http://www.dhtmlgoodies.com/

quinta-feira, 26 de maio de 2011

melhor GUI toolkit para web

GWT (google web toolkit) na minha opinião é a solução mais eficiciente para implementar a camada visual de um sistema Web. Nao somente pela simplificade de criar RIA para web mas pelo fato de usar melhor o processamento na camada client, aumentando a escalabilidade do seu sistema. Pode usar facilmente o pattern MVP ao inves do MVC facilitando os testes unitarios e melhorando o reuso.

Para ser produtivo recomendo usar algum outro framework como ext-GWT ou smartGWT (antigo GWT-Ext) como uma camada adicional assim ganhara uma interface muito rica, com varios widgets muito legais, pronto para uso. Muita discussao tem em torno qual é o melhor, ext-GWT ou Smart. O Smart aparenta ser um produto mais maduro porem o ext foi desenvolvido pela mesma empresa que fez o Ext JS portanto tem credibilidade. O mais importante é nao misturar os Widgets no mesmo projeto, nem mesmo com os nativos do GWT pois tivemos alguns problemas de comportamento quando tentamos isso.

O GWT abstrai toda a complexidade da implementação Javascript client-side. Possui uma ferramenta de designer de interfaces GUI muito boa para o Eclipse. O GWT nao resolve a camada de negocio ou persistencia, para isso tem outros frameworks e patterns. Como procuro criar metodos stateless o GWT é perfeito para manter os estados no cliente sem suar muito. Um colega relatou a experiencia de transformar um sistema inteiro que estava em Struts-2 (argh!) para uma nova arquitetura usando GWT + SmartGWT, sem manter absolutamente nenhum estado no servidor e transferindo parte do processamento (validações, calculos, etc..) para a camada client. O ambiente de produção que contava com 3 servidores em cluster, operando a 60% da capacidade de processamento em determinados horarios caiu hoje para 1 unico servidor (+1 de failover) e atendendo a mesma demanda opera no maximo a 40% de capacidade. Recebeu elogios dos usuarios referente a usabilidade e tempo de resposta.

O potencial é tao grande que existe até uma implementacao do Quake-2 em GWT:

school timetable, rostering nurses, cutting stock and other planning optimization

If you have these kinds of challenges, the answer is Jboss Drools Planner. Like all JBoss product is easy to install, learn and use. The coolest thing about JBoss projects is that the code is open and anyone can verify the implementation and adapt to their needs.
Obviously I chose the long way. I prefer to download the source, compile, make a walkthrough into the source code and understand how the project is structured. I always learn new techniques and concepts in this way. After spending a few hours resolving conflicts with some libs finally the server started running a hello-world project. Some project ideas that I have can go faster with Drools. More information? Check out at Drools website - http://www.jboss.org/drools/drools-planner

quarta-feira, 25 de maio de 2011

Document classification with Naive Bayes classifier

Uma solução viavel para a classificação de documentos é o algoritmo de Naive Bayes. Classificadores Naive Bayes pode ser treinado de forma muito eficiente em um ambiente de aprendizado supervisionado.
O maior desafio foi codificar os objetos de suporte que possibilitarao o aprendizado e melhoramento continuo dos padrões, efetuando novos mapeamento sob demanda.
O objetivo deste projeto é automatizar 100% das tarefas, eliminando a necessidade de monitorar e treinar para novos padrões de documentos. Os proprios usuarios estarão trabalhando para o continuo aprendizado da rede. Por isso uma tecnica estatistica foi usada para que a classificação ocorresse analisando tendencias submetida a uma arvore de decisão com opção de rankeamento para cada usuario/conjunto de informações.
Apos estar proximo de concluir o projeto, lendo alguns artigos, encontrei algumas tecnicas mais promissoras como Gradient Boosting. Dizem que o seu projeto/produto esta obsoleto no dia que esta pronto mas desta vez ficou obsoleto antes de terminar :o(
Um outro desafio foi criar os spiders para extrair as informações dos mais diversos tipos de documentos. Por exemplo, em um sistema de contabilidade temos alguns documentos como dacon, dctf, dipj, darf, das, contratos sociais, balanços, sped, sefip, etc, etc, etc... que podem estar em diferentes formatos digitais como PDF, Doc, XLS, JPeg, Tiff, etc..
A premissa é receber os documentos, sabendo ja o ambiente de negócio, chavear para um subset de padrões extraindo os atributos para a classificação e submeter ao algoritmo.
Os documentos em PDF usei algumas libs do projeto apache, ja para o OCR o Tesseract, atualmente suportado pelo Google e acredito que é o mesmo OCR usado no GDocs. Precisei criar um dicionario para o pt-BR na versão 3.0 e nao consegui ainda ajusta-lo para um resultado satisfatório para documentos em resolução não muito boa, obrigando a scannear com 300DPI. Como 300DPI não é nada muito excessivo esta tarefa virou um `spike` de baixa prioridade. A implementação do algoritmo é trivial, existem varia bibliotecas prontas mas preferi fazer a minha própria.

quarta-feira, 15 de dezembro de 2010

Our Dell server crashed today

One of our Dell server crashed today after almost four years on, working around the clock 24x7. In one minute I was attended by technical support that was one of the best support I have received over the phone in years. Are to be congratulated for their quality service and products. The server has reached the end of useful life and we'll buy another Dell again, they have the best servers. I recommend it!

sábado, 11 de dezembro de 2010

Error: java.lang.NoSuchMethodError: org.mortbay.thread.Timeout.

After installing the 2.1 GWT (google web toolkit) in Eclipse received the following error:

Exception in thread "main" java.lang.NoSuchMethodError: org.mortbay.thread.Timeout.(Ljava/lang/Object;)V
at org.mortbay.io.nio.SelectorManager$SelectSet.(SelectorManager.java:306)
at org.mortbay.io.nio.SelectorManager.doStart(SelectorManager.java:223)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at org.mortbay.jetty.nio.SelectChannelConnector.doStart(SelectChannelConnector.java:303)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at org.mortbay.jetty.Server.doStart(Server.java:233)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at com.google.gwt.dev.shell.jetty.JettyLauncher.start(JettyLauncher.java:542)
at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:431)
at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1053)
at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:795)
at com.google.gwt.dev.DevMode.main(DevMode.java:282)

This indicates duplicate libraries.

To solve:
In eclipse, Press CTRL + SHIFT + T and search for: org.mortbay.io.nio.SelectorManager

Probably you will find more than one reference.
In my environment were found two:
-> com.google.appengine.eclipse.sdkbundle.1.3.8_1.3.8.v201010161055
-> com.google.gwt.eclipse.sdkbundle.2.1.0_2.1.0.v201010280102


In Java:
- 50% of problems are classpath,
- the other 50% of the problems resides in your own code
- and the remaining 50% is with the user ;o)

domingo, 22 de agosto de 2010

XML DIFF - Show differences in XML

This little routine will display the difference between 2 xml files analyzing the contents and returning a list of differences. There is a limitation with respect to the structure of XML to be compared
Depending on the content and structure of XML you will need to make some modifications in the implementation. In the comparison of XML that contains collections the analysis is done element by element and if there is an element out of order the return can not be expected. Ideally, the XML should be typed ( following a XMLSchema ).


Class TechParseCounter:
/**
* Desenvolvido por Ricardo Alberto Harari em 05/06/2005 - 20:43 - GMT-3:00
*
* Este codigo pode ser usado livremente, inclusive para fins comerciais
* desde que mantenha referencia ao autor original e não altere o fully qualified name desta classe
*
* Technique T.I. Ltda
* www.technique.com.br
*
* @author Ricardo A. Harari
*
*/

package com.technique.xmlUtil;

public class TechParseCounter {
private int counter;
public TechParseCounter(int i) {
counter = i;
}
public void add() {
counter++;
}
public int getCounter() {
return counter;
}
}



CLASS TechParseContentItem
/**
* Desenvolvido por Ricardo Alberto Harari em 05/06/2005 - 20:43 - GMT-3:00
*
* Este codigo pode ser usado livremente, inclusive para fins comerciais
* desde que mantenha referencia ao autor original e não altere o fully qualified name desta classe
*
* Technique T.I. Ltda
* www.technique.com.br
*
* @author Ricardo A. Harari
*
*/

package com.technique.xmlUtil;

public class TechParseContentItem {
static String[] ACTION_NAMES = new String[] {
"insert",
"update",
"delete"
};

public static int ACTION_INSERT = 0;
public static int ACTION_UPDATE = 1;
public static int ACTION_DELETE = 2;

public int action;

public String key;
public String attributeName;
public String oldValue;
public String newValue;

public TechParseContentItem(int action, String key, String attributeName, String oldValue, String newValue) {
this.action = action;
this.key = key;
this.attributeName = attributeName;
this.oldValue = oldValue;
this.newValue = newValue;
}

public String actionName() {
return ACTION_NAMES[action];
}

public String toString() {
String act = action == ACTION_INSERT ? "insert" : action == ACTION_UPDATE ? "update" : "delete";
return "action=[" + act + "], key=[" + key + "], attributeName=[" + attributeName + "], oldValue=[" + oldValue + "], newValue=[" + newValue + "]";
}

}


CLASS TechParseDiff
/**
* Desenvolvido por Ricardo Alberto Harari em 05/06/2005 - 20:43 - GMT-3:00
*
* Este codigo pode ser usado livremente, inclusive para fins comerciais
* desde que mantenha referencia ao autor original e não altere o fully qualified name desta classe
*
* Technique T.I. Ltda
* www.technique.com.br
*
* @author Ricardo A. Harari
*
*/

package com.technique.xmlUtil;

/**
* Technique T.I. Ltda
* www.technique.com.br
*
*
*/

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class TechParseDiff extends DefaultHandler
{
/*
* separador de campos para montar a chave
*/
static String field_separator = "@5@_@<>@~5";
static String field_separator2 = "/";

private Hashtable ocorrencias = null;
private String[] levelBuffer = new String[100];
private String currentLevel = null;
private int level = 0;
private Hashtable xmlcontent = null;
private boolean started = false;
private StringBuffer currentCharBuffer = null;

public Hashtable retrieveDifference() {
return xmlcontent;
}

public TechParseDiff() {
super();
level = 0;
}

private void clearBuffer() {
currentCharBuffer = new StringBuffer();
started = false;
xmlcontent = new Hashtable();
level = 0;
currentLevel = "";
ocorrencias = new Hashtable();
}

protected void relaseBuffer() {
ocorrencias = null;
xmlcontent = null;
currentCharBuffer = null;
currentLevel = null;
}
private String formatKey(String key) {
return key.replaceAll(field_separator, field_separator2);
}
private String formatAttributeName(String key) {
int i = key.lastIndexOf(field_separator) + field_separator.length();
return key.substring(i, key.length());
}

/**
* compara 2 XMLs e coloca no Hashtable "xmlcontent" o resultado das diferencas encontradas.
* em xmlcontent voce terá objetos do tipo TechParseContentItem
*
* @param oldXml - stream do xml antigo
* @param newXml - stream do xml novo
* @throws SAXException - erro de parsing
* @throws IOException - erro de IO
*/
public void compare(InputStream oldXml, InputStream newXml) throws Exception {
try {
clearBuffer();
Hashtable difference = new Hashtable();
XMLReader xr = XMLReaderFactory.createXMLReader();
InputSource is = new InputSource(oldXml);
xr.setContentHandler(this);
xr.setErrorHandler(this);
xr.parse(is);
Hashtable ht1 = xmlcontent;

this.clearBuffer();
is = new InputSource(newXml);
xr.setContentHandler(this);
xr.setErrorHandler(this);
xr.parse(is);

Hashtable ht2 = xmlcontent;
Enumeration enum2 = ht2.keys();
//int inserts = 0;
while (enum2.hasMoreElements()) {
Object key2 = enum2.nextElement();
Object o2 = ht2.get(key2);
Object o1 = ht1.get(key2);
if (o1 == null) {
difference.put(key2, new TechParseContentItem(TechParseContentItem.ACTION_INSERT,
formatKey(key2.toString()),
formatAttributeName(key2.toString()),
null,
o2.toString()));
} else {
if (!o1.toString().equals(o2.toString())) {
difference.put(key2, new TechParseContentItem(TechParseContentItem.ACTION_UPDATE,
formatKey(key2.toString()),
formatAttributeName(key2.toString()),
o1.toString(),
o2.toString()));
}
ht1.remove(key2);
}
}
enum2 = ht1.keys();
while (enum2.hasMoreElements()) {
Object key1 = enum2.nextElement();
Object o1 = ht1.get(key1);
difference.put(key1, new TechParseContentItem(TechParseContentItem.ACTION_DELETE,
formatKey(key1.toString()),
formatAttributeName(key1.toString()),
o1.toString(),
null));
}
this.clearBuffer();
xmlcontent = difference;
} catch (Exception e) {
throw new Exception("Nao foi possivel gravar os dados de Log. Motivo:" + e.getMessage(), e);
}
}

public void endElement (String uri, String name, String qName) {
//super.endElement(uri, name, qName);
if ("".equals (uri)) {
removeLevel(qName);
} else {
removeLevel("{" + uri + "}" + name);
}
started = false;
}

private void addLevel(String name) {
levelBuffer[++level] = name;
currentLevel += field_separator + name;
Object o = ocorrencias.get(currentLevel);
if (o == null) {
ocorrencias.put(currentLevel, new TechParseCounter(1));
} else {
((TechParseCounter) o).add();
}
}

private void removeLevel(String name) {
if (currentCharBuffer.length() > 0) {
TechParseCounter ocorr = (TechParseCounter) ocorrencias.get(currentLevel);
String s = ocorr.getCounter() == 0 ? "" : "[" + ocorr.getCounter() + "]";
xmlcontent.put(currentLevel
+ s
+ field_separator + name,
currentCharBuffer.toString().trim());
currentCharBuffer = new StringBuffer();
}
String levelName = levelBuffer[level];
int j = currentLevel.length() - levelName.length() - field_separator.length();
currentLevel = j < 1 ? "" : currentLevel.substring(0, j);
levelBuffer[level--] = null;
}

public void startElement (String uri, String name, String qName, Attributes atts) {
if ("".equals (uri)) {
addLevel(qName);
} else {
addLevel("{" + uri + "}" + name);
}
int i = atts.getLength();
TechParseCounter ocorr = (TechParseCounter) ocorrencias.get(currentLevel);
String s = ocorr.getCounter() == 0 ? "" : "[" + ocorr.getCounter() + "]";
for (int j = 0; j < i; j++) {
if (atts.getValue(j) != null) {
xmlcontent.put(currentLevel + s + field_separator + atts.getQName(j), atts.getValue(j));
}
}
started = true;
}

public void characters (char ch[], int start, int length) {
if (!started) return;
for (int i = start; i < start + length; i++) {
switch (ch[i]) {
case '\\'|'"'|'\r'|'\n'|'\t':
break;
default:
currentCharBuffer.append(ch[i]);
break;
}
}
}

public void startDocument () {
//start
}

public void endDocument () {
//end
}

}



Sample Usage:
/**
* Technique TI Ltda - Project: techEngine
* @author Ricardo A. Harari
* com.technique.xmlUtil
*
* xml diff sample usage
*/

package com.technique.xmlUtil;

import java.io.ByteArrayInputStream;
import java.util.Enumeration;
import java.util.Hashtable;

public class TechDiffSample {

/**
* @param args
*/
public static void main(String[] args) {
String xml1 = "<document><stockoption>PETR4</stockoption><date>05/06/2005</date><value>1.20</value><stockoption>NET4</stockoption><date>04/06/2005</date><value>1.20</value></document>";
String xml2 = "<document><stockoption>PETR4</stockoption><date>05/06/2005</date><value>1.22</value><comment>ipsenlorem</comment><stockoption>NET4</stockoption><date>05/06/2005</date><value>1.20</value></document>";
TechParseDiff xmldiff = new TechParseDiff();
ByteArrayInputStream bais1 = new ByteArrayInputStream(xml1.getBytes());
ByteArrayInputStream bais2 = new ByteArrayInputStream(xml2.getBytes());
try {
xmldiff.compare(bais1, bais2);
xmldiff.toString();
Hashtable ht = xmldiff.retrieveDifference();
Enumeration en = ht.elements();
while (en.hasMoreElements()) {
TechParseContentItem item = (TechParseContentItem) en.nextElement();
System.out.println(item.toString());
}
} catch (Exception e) {
e.printStackTrace();
}

}

}


The result will show the differences between the two XML indicating the insert, updates and deletes. If you have collections in XML you will also have information on the order of the element [1, 2, 3, ...] that has changed.

action=[insert], key=[/document/comment[1]/comment], attributeName=[comment], oldValue=[null], newValue=[ipsenlorem]
action=[update], key=[/document/value[1]/value], attributeName=[value], oldValue=[1.20], newValue=[1.22]

action=[update], key=[/document/date[2]/date], attributeName=[date], oldValue=[04/06/2005], newValue=[05/06/2005]

This routine uses the SAX parser, so it can be used to compare huge files. With a little modification you can record the results of the comparisons in a database or files instead of storing in a hashtable.
This routine is part of an old framework I developed, the TechEngine.


Have fun!

domingo, 8 de agosto de 2010

JBoss JBPM - Generating an image at runtime (JPDL -> PNG)

This article is an example of how to dynamically generate an image of a process JBPM.
JBoss JBPM and other BPM tools make use of XML to describe the business processes.
The concept is then to do a parsing of the XML and generate a process image at runtime.
The original idea was obtained from a Chinese discussion group.
In this article, Mr. Yeyong presents a simple solution to generate at run-time an image of a process modeled in JBPM, parsing the JPDL.xml and using the resources of elementary geometry with AWT lib.
Using the same idea and reusing the source code of Mr. Yeyong I extend this concept to provide a visual representation of the steps already completed of the process.
The original article can be found at the following URL: http://jbpm.group.javaeye.com/group/blog/470760?page=2

The following is the source code which is basically composed of five classes

/**
* Technique TI Ltda - Project: PlanetaContabilWeb - www.planetacontabil.com.br
* @author yeyong - http://jbpm.group.javaeye.com/group/blog/470760?page=2
* @author Ricardo A. Harari - ricardo.harari@gmail.com
* @date 25/01/2010 12:18:45
* br.com.technique.process.render.graph
*
* TODO
*/

package br.com.technique.process.render.graph;

import java.awt.Point;
import java.awt.Rectangle;

/**
* @author yeyong
*
*/
public class GeometryUtils {
/**
 * ????(x1,y1)-(x2,y2)???
 *
 * @param x1
 * @param y1
 * @param x2
 * @param y2
 * @return
 */
public static double getSlope(int x1, int y1, int x2, int y2) {
  return ((double) y2 - y1) / (x2 - x1);
}

/**
 * ????(x1,y1)-(x2,y2)?y???
 *
 * @param x1
 * @param y1
 * @param x2
 * @param y2
 * @return
 */
public static double getYIntercep(int x1, int y1, int x2, int y2) {
  return y1 - x1 * getSlope(x1, y1, x2, y2);
}
/**
 * ???????
 *
 * @param rect
 * @return
 */
public static Point getRectangleCenter(Rectangle rect) {
  return new Point((int) rect.getCenterX(), (int) rect.getCenterY());
}

/**
 * ??????p0?p1?????????
 *
 * @param rectangle
 * @param p1
 * @return
 */
public static Point getRectangleLineCrossPoint(Rectangle rectangle, Point p1, int grow) {
  Rectangle rect = rectangle.getBounds();
  rect.grow(grow, grow);
  Point p0 = GeometryUtils.getRectangleCenter(rect);

  if (p1.x == p0.x) {
    if (p1.y < y ="="" slope =" GeometryUtils.getSlope(p0.x," slopeline =" GeometryUtils.getSlope(p0.x," yintercep =" GeometryUtils.getYIntercep(p0.x,"> slope - 1e-2) {
    if (p1.y < page="2"> nodes = new LinkedHashMap();
public static final int RECT_OFFSET_X = -7;
public static final int RECT_OFFSET_Y = -8;
public static final int DEFAULT_PIC_SIZE = 48;

/** R.Harari - activities list */
public Hashtable listActivities;


private final static Map nodeInfos = new HashMap();
static {
  nodeInfos.put("start", "start_event_empty.png");
  nodeInfos.put("end", "end_event_terminate.png");
  nodeInfos.put("end-cancel", "end_event_cancel.png");
  nodeInfos.put("end-error", "end_event_error.png");
  nodeInfos.put("decision", "gateway_exclusive.png");
  nodeInfos.put("fork", "gateway_parallel.png");
  nodeInfos.put("join", "gateway_parallel.png");
  nodeInfos.put("state", null);
  nodeInfos.put("hql", null);
  nodeInfos.put("sql", null);
  nodeInfos.put("java", null);
  nodeInfos.put("script", null);
  nodeInfos.put("task", null);
  nodeInfos.put("sub-process", null);
  nodeInfos.put("custom", null);
}

public JpdlModel(InputStream is) throws Exception {
  this(new SAXReader().read(is).getRootElement());
}

public JpdlModel(InputStream is, List listHistoryActivities) throws Exception {
    this(new SAXReader().read(is).getRootElement());
    if (listHistoryActivities != null) {
     listActivities = new Hashtable();
     for (HistoryActivityInstance hai : listHistoryActivities) {
      listActivities.put(hai.getActivityName(), hai);
     }
    }
}

@SuppressWarnings("unchecked")
private JpdlModel(Element rootEl) throws Exception {
  for (Element el : (List) rootEl.elements()) {
    String type = el.getQName().getName();
    if (!nodeInfos.containsKey(type)) { // ????????
      continue;
    }
    String name = null;
    if (el.attribute("name") != null) {
      name = el.attributeValue("name");
    }
    String[] location = el.attributeValue("g").split(",");
    int x = Integer.parseInt(location[0]);
    int y = Integer.parseInt(location[1]);
    int w = Integer.parseInt(location[2]);
    int h = Integer.parseInt(location[3]);

    if (nodeInfos.get(type) != null) {
      w = DEFAULT_PIC_SIZE;
      h = DEFAULT_PIC_SIZE;
    } else {
      x -= RECT_OFFSET_X;
      y -= RECT_OFFSET_Y;
      w += (RECT_OFFSET_X + RECT_OFFSET_X);
      h += (RECT_OFFSET_Y + RECT_OFFSET_Y);
    }
    Node node = new Node(name, type, x, y, w, h);
    parserTransition(node, el);
    nodes.put(name, node);
  }
}

@SuppressWarnings("unchecked")
private void parserTransition(Node node, Element nodeEl) {
  for (Element el : (List) nodeEl.elements("transition")) {
    String label = el.attributeValue("name");
    String to = el.attributeValue("to");
    Transition transition = new Transition(label, to);
    String g = el.attributeValue("g");
    if (g != null && g.length() > 0) {
      if (g.indexOf(":") < p =" g.split(" lines =" p[0].split(" exp ="="" p =" exp.split("> getNodes() {
  return nodes;
}
  public static Map getNodeInfos() {
  return nodeInfos;
}

}


/**
* Technique TI Ltda - Project: PlanetaContabilWeb - www.planetacontabil.com.br
* @author yeyong - http://jbpm.group.javaeye.com/group/blog/470760?page=2
* @author Ricardo A. Harari - ricardo.harari@gmail.com - improved to represent completed steps of a running process
* @date 25/01/2010 12:17:04
* br.com.technique.process.render.graph
*
* TODO
*/

package br.com.technique.process.render.graph;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import org.jbpm.api.history.HistoryActivityInstance;

/**
* @author
*
*/
public class JpdlModelDrawer {
public static final int RECT_OFFSET_X = JpdlModel.RECT_OFFSET_X;
public static final int RECT_OFFSET_Y = JpdlModel.RECT_OFFSET_Y;
public static final int RECT_ROUND = 15;

public static final int DEFAULT_FONT_SIZE = 12;

public static final Color DEFAULT_STROKE_COLOR = Color.decode("#03689A");
public static final Stroke DEFAULT_STROKE = new BasicStroke(2);

public static final Color DEFAULT_LINE_STROKE_COLOR = Color.decode("#808080");
public static final Stroke DEFAULT_LINE_STROKE = new BasicStroke(1);

public static final Color DEFAULT_FILL_COLOR = Color.decode("#F6F7FF");

/** R.Harari - nova cores para representar o estado das etapas */
public static final Color DEFAULT_FILL_COLOR_FINISHED = Color.decode("#C4FFC1");
public static final Color DEFAULT_FILL_COLOR_CURRENT = Color.decode("#FFFF97");
/** */


private final static Map nodeInfos = JpdlModel.getNodeInfos();

public BufferedImage draw(JpdlModel jpdlModel) throws IOException {
  Rectangle dimension = getCanvasDimension(jpdlModel);
  BufferedImage bi = new BufferedImage(dimension.width, dimension.height, BufferedImage.TYPE_INT_ARGB);
  Graphics2D g2 = bi.createGraphics();
  g2.setColor(Color.WHITE);
  g2.fillRect(0, 0, dimension.width, dimension.height);
  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  Font font = new Font("Arial", Font.PLAIN, DEFAULT_FONT_SIZE);
  g2.setFont(font);
  Map nodes = jpdlModel.getNodes();
  drawNode(nodes, g2, font, jpdlModel.listActivities);
  drawTransition(nodes, g2);
  return bi;
}

/**
 * ?????????
 *
 * @return
 */
private Rectangle getCanvasDimension(JpdlModel jpdlModel) {
  Rectangle rectangle = new Rectangle();
  Rectangle rect;
  for (Node node : jpdlModel.getNodes().values()) {
    rect = node.getRectangle();
    if (rect.getMaxX() > rectangle.getMaxX()) {
      rectangle.width = (int) rect.getMaxX();
    }
    if (rect.getMaxY() > rectangle.getMaxY()) {
      rectangle.height = (int) rect.getMaxY();
    }
    for (Transition transition : node.getTransitions()) {
      List trace = transition.getLineTrace();
      for (Point point : trace) {
        if (rectangle.getMaxX() < width =" point.x;" height =" point.y;"> nodes, Graphics2D g2) throws IOException {
  g2.setStroke(DEFAULT_LINE_STROKE);
  g2.setColor(DEFAULT_LINE_STROKE_COLOR);
  for (Node node : nodes.values()) {
    for (Transition transition : node.getTransitions()) {
      String to = transition.getTo();
      Node toNode = nodes.get(to);
      List trace = new LinkedList(transition.getLineTrace());
      int len = trace.size() + 2;
      trace.add(0, new Point(node.getCenterX(), node.getCenterY()));
      trace.add(new Point(toNode.getCenterX(), toNode.getCenterY()));
      int[] xPoints = new int[len];
      int[] yPoints = new int[len];
      for (int i = 0; i < taskgrow =" 4;" smallgrow =" -2;" grow =" 0;" grow =" smallGrow;" grow =" taskGrow;" p =" GeometryUtils.getRectangleLineCrossPoint(node.getRectangle()," grow =" smallGrow;" grow =" taskGrow;" p =" GeometryUtils.getRectangleLineCrossPoint(toNode.getRectangle()," label =" transition.getLabel();"> 0) {
        int cx, cy;
        if (len % 2 == 0) {
          cx = (xPoints[len / 2 - 1] + xPoints[len / 2]) / 2;
          cy = (yPoints[len / 2 - 1] + yPoints[len / 2]) / 2;
        } else {
          cx = xPoints[len / 2];
          cy = yPoints[len / 2];
        }
        Point labelPoint = transition.getLabelPosition();
        if (labelPoint != null) {
          cx += labelPoint.x;
          cy += labelPoint.y;
        }
        cy -= RECT_OFFSET_Y + RECT_OFFSET_Y / 2;
        g2.drawString(label, cx, cy);
      }
    }
  }
}

private void drawArrow(Graphics2D g2, int x1, int y1, int x2, int y2) {
  final double len = 8.0;
  double slopy = Math.atan2(y2 - y1, x2 - x1);
  double cosy = Math.cos(slopy);
  double siny = Math.sin(slopy);
  int[] xPoints = { 0, x2, 0 };
  int[] yPoints = { 0, y2, 0 };
  double a = len * siny, b = len * cosy;
  double c = len / 2.0 * siny, d = len / 2.0 * cosy;
  xPoints[0] = x2 - (int) (b + c);
  yPoints[0] = y2 - (int) (a - d);
  xPoints[2] = x2 - (int) (b - c);
  yPoints[2] = y2 - (int) (d + a);
  
  g2.fillPolygon(xPoints, yPoints, 3);
}

/**
 * @param g2
 * @throws IOException
 */
private void drawNode(Map nodes, Graphics2D g2, Font font, Hashtable listActivities) throws IOException {
  for (Node node : nodes.values()) {
    String name = node.getName();

    if (nodeInfos.get(node.getType()) != null) {
      BufferedImage bi2 = ImageIO.read(getClass().getResourceAsStream(
          "icons/48/" + nodeInfos.get(node.getType())));
      g2.drawImage(bi2, node.getX(), node.getY(), null);
    } else {
      int x = node.getX();
      int y = node.getY();
      int w = node.getWitdth();
      int h = node.getHeight();
    
      HistoryActivityInstance hai = null;
      Color fillColor = DEFAULT_FILL_COLOR;

      if (listActivities != null) {
       hai = listActivities.get(name);
       if (hai != null) {
         if (hai.getEndTime() != null) {
          fillColor = DEFAULT_FILL_COLOR_FINISHED;
         } else {
          fillColor = DEFAULT_FILL_COLOR_CURRENT;
         }
       }
      }

      g2.setColor(fillColor);
      g2.fillRoundRect(x, y, w, h, RECT_ROUND, RECT_ROUND);
      g2.setColor(DEFAULT_STROKE_COLOR);
      g2.setStroke(DEFAULT_STROKE);
      g2.drawRoundRect(x, y, w, h, RECT_ROUND, RECT_ROUND);

      FontRenderContext frc = g2.getFontRenderContext();
      Rectangle2D r2 = font.getStringBounds(name, frc);
      int xLabel = (int) (node.getX() + ((node.getWitdth() - r2.getWidth()) / 2));
      int yLabel = (int) ((node.getY() + ((node.getHeight() - r2.getHeight()) / 2)) - r2.getY());
      g2.setStroke(DEFAULT_LINE_STROKE);
      g2.setColor(Color.black);
      g2.drawString(name, xLabel, yLabel);
    }
  }
}
}


/**
* Technique TI Ltda - Project: PlanetaContabilWeb - www.planetacontabil.com.br
* @author yeyong - http://jbpm.group.javaeye.com/group/blog/470760?page=2
* @author Ricardo A. Harari - ricardo.harari@gmail.com
* @date 25/01/2010 12:13:40
* br.com.technique.process.render.graph
*
* TODO
*/

package br.com.technique.process.render.graph;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;

public class Node {
private String name;
private String type;
private Rectangle rectangle;
private List transitions = new ArrayList();

public Node(String name, String type) {
  this.name = name;
  this.type = type;
}

public Node(String name, String type, int x, int y, int w, int h) {
  this.name = name;
  this.type = type;
  this.rectangle = new Rectangle(x, y, w, h);
}

public Rectangle getRectangle() {
  return rectangle;
}

public void setRectangle(Rectangle rectangle) {
  this.rectangle = rectangle;
}

public String getType() {
  return type;
}

public void setType(String type) {
  this.type = type;
}

public String getName() {
  return name;
}

public void setName(String name) {
  this.name = name;
}

public void addTransition(Transition transition) {
  transitions.add(transition);
}

public List getTransitions() {
  return transitions;
}

public void setTransitions(List transitions) {
  this.transitions = transitions;
}

public int getX() {
  return rectangle.x;
}

public int getY() {
  return rectangle.y;
}

public int getCenterX() {
  return (int) rectangle.getCenterX();
}

public int getCenterY() {
  return (int) rectangle.getCenterY();
}

public int getWitdth() {
  return rectangle.width;
}

public int getHeight() {
  return rectangle.height;
}
}


/**
* Technique TI Ltda - Project: PlanetaContabilWeb - www.planetacontabil.com.br
* @author yeyong - http://jbpm.group.javaeye.com/group/blog/470760?page=2
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
* @date 25/01/2010 12:14:48
* br.com.technique.process.render.graph
*
* TODO
*/

package br.com.technique.process.render.graph;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class Transition {
private Point labelPosition;
private List lineTrace = new ArrayList();
private String label;
private String to;

public Transition(String label, String to) {
  this.label = label;
  this.to = to;
}

public Point getLabelPosition() {
  return labelPosition;
}

public void setLabelPosition(Point labelPosition) {
  this.labelPosition = labelPosition;
}

public List getLineTrace() {
  return lineTrace;
}

public void setLineTrace(List lineTrace) {
  this.lineTrace = lineTrace;
}

public void addLineTrace(Point lineTrace) {
  if (lineTrace != null) {
    this.lineTrace.add(lineTrace);
  }
}

public String getLabel() {
  return label;
}
public void setLabel(String label) {
  this.label = label;
}

public String getTo() {
  return to;
}

public void setTo(String to) {
  this.to = to;
}

}  



Example of use:

JpdlModel jpdlModel = new JpdlModel(JbpmAberturaEmpresa.class.getResourceAsStream("aberturaEmpresa.jpdl.xml"), hai);
ImageIO.write(new JpdlModelDrawer().draw(jpdlModel), "png", new File("/tmp/myprocess.png"));

aberturaEmpresa.jpdl.xml -> is my business process located at the same package of JbpmAberturaEmpresa class. You should customize to retrieve your business process.
hai -> History activities - see bellow a method to retrieve the activities
/tmp/myprocess.png -> output path+file of the PNG image

Retrieving the history activities:

String execID = "";
/** if you are running as a java application - outside a j2ee container */
ProcessEngine processEngine = new Configuration().setResource("jbpm.cfg.xml").buildProcessEngine();

List hai = processEngine.getHistoryService().createHistoryActivityInstanceQuery()
.processInstanceId(execID)
.list();


Below is an example of generated image:
Green -> completed steps
Yellow -> current step
White -> uncompleted step




Was tested with the latest version of JBPM 4.4 and works normally.
The following is a direct access to source code containing also the icons. The icons are extracted from a JAR used by the modeler of the eclipse ide.

https://drive.google.com/open?id=14OKHx0EkrgsB2zrP62t1OGjKK9lK5fNX

quarta-feira, 12 de novembro de 2008

Codigo do Algoritmo das 8 Rainhas

Parece que os codigos apresentam problemas quando postados aqui.
Bom, ai vai o codigo das 8 rainhas. Se mais alguem precisar de algum codigo antigo publicado aqui me avisa....devo ter ainda em algum lugar do HD....tambem prometo que vou me dedicar mais a este blog em breve, atualmente estou com outras prioriodades e portanto sem tempo de publicar novos artigos aqui.




import java.util.HashSet;

import java.util.Random;

import java.util.Set;



/**

* @author Ricardo Alberto Harari - ricardo.harari@gmail.com

*

*

*/

public class OitoRainhas {



private static final int MAX_FITNESS = 8;

private static Random randomico = new Random();



// parametros de stress test

private static boolean ocorreuFalha = false;

private static int totalMaxIteracoes = 0;

private static int totalIteracoes = 0;

private static boolean disablePrint = false;



public static void main(String[] args) {

go();

}



/**

* inicio do processo

*/

private static void go() {

long startTime = System.currentTimeMillis();

println("Populacao Inicial");



// gerar a populacao inicial com 10 individuos

Set populacao = new HashSet();

carregarPopulacao(populacao);

println("------------------------------");



double probabilidadeMutacao = 0.15;

int maxIteracoes = 300000;



String melhorIndividuo = null;

boolean achouSolucao = false;

int bestFitness = 0;



int i = 0;

int counter = 0;



for (i = 0; i < maxIteracoes; i++) {

melhorIndividuo = geneticAlgorithm(populacao, probabilidadeMutacao,

bestFitness);

int ftness = fitness(melhorIndividuo);

if (ftness > bestFitness) {

probabilidadeMutacao = 0.10;

counter = 0;

println("novo fitness = " + ftness);

bestFitness = ftness;

if (ftness == MAX_FITNESS) {

achouSolucao = true;

break;

}

} else {

counter++;

if (counter > 1000) {

probabilidadeMutacao = 0.30;

} else if (counter > 2000) {

probabilidadeMutacao = 0.50;

} else if (counter > 5000) {

populacao.clear();

carregarPopulacao(populacao);

probabilidadeMutacao = 0.10;

bestFitness = -1;

}

}

}



println("------------------------------");



if (achouSolucao) {

println("Solucao encontrada em " + i + " iteracoes");

println("Solucao =" + melhorIndividuo);

println("Fitness =" + fitness(melhorIndividuo));

} else {

System.out.println("Solucao nao encontrada após " + i

+ " iteracoes");

System.out.println("Melhor Individuo =" + melhorIndividuo);

System.out.println("Fitness =" + fitness(melhorIndividuo));

ocorreuFalha = true;

}



totalIteracoes += i;

if (i > totalMaxIteracoes)

maxIteracoes = i;



println("------------------------------");



mostrarTabuleiro(melhorIndividuo);

println("Elapsed time = " + (System.currentTimeMillis() - startTime)

+ "ms");

}



/**

* @param string

*/

private static void println(String string) {

if (!disablePrint)

System.out.println(string);

}



/**

* @param populacao

*/

private static void carregarPopulacao(Set populacao) {

while (populacao.size() < 10) {

String individuo = gerarIndividuo(8);

println("individuo=" + individuo);

populacao.add(individuo);

}

}



/**

* mostrar o tabuleiro graficamente

*

* @param melhorIndividuo

* @return

*/

private static void mostrarTabuleiro(String melhorIndividuo) {

println("|---+---+---+---+---+---+---+---|");

for (int i = 0; i < 8; i++) {

print("|");

int posicaoRainha = Integer.parseInt(melhorIndividuo.substring(i,

i + 1)) - 1;

for (int j = 0; j < 8; j++) {

if (posicaoRainha == j) {

print(" x |");

} else {

print(" |");

}

}

println("\r\n|---+---+---+---+---+---+---+---|");

}

}



/**

* @param string

*/

private static void print(String string) {

if (!disablePrint)

System.out.print(string);



}



/**

* logica GA mantendo os melhores na populacao retorna o melhor individuo

*

* @param populacao

* @param probabilidadeMutacao

*/

private static String geneticAlgorithm(Set populacao,

double probabilidadeMutacao, int fitnessAtual) {

String melhor = null;

Set filhos = new HashSet();

int tamanhoPopulacao = populacao.size();



while (filhos.size() < tamanhoPopulacao) {

String p1 = selecionarAleatorio(populacao, "");

String p2 = selecionarAleatorio(populacao, p1);

String filho = crossover(p1, p2);

if (randomico.nextDouble() <= probabilidadeMutacao) {

int ffitness = fitness(filho);

if (ffitness <= fitnessAtual)

filho = mutate(filho);

}

filhos.add(filho);

}



// adicionar dois dos melhores pais

Object[] pais = populacao.toArray();

int[] f = new int[pais.length];

int melhorF = -1;

for (int i = 0; i < pais.length; i++) {

f[i] = fitness((String) pais[i]);

if (melhorF < f[i]) {

melhorF = f[i];

}

}

populacao.clear();

while (populacao.size() < 2) {

for (int i = 0; i < f.length; i++) {

if (f[i] == melhorF) {

populacao.add((String) pais[i]);

}

if (populacao.size() == 2)

break;

}

melhorF--;

}



filhos.addAll(populacao);

Object[] pop = filhos.toArray();

f = new int[pop.length];

melhorF = -1;

for (int i = 0; i < f.length; i++) {

f[i] = fitness((String) pop[i]);

if (melhorF < f[i]) {

melhorF = f[i];

melhor = (String) pop[i];

}

}

populacao.clear();

while (populacao.size() < tamanhoPopulacao) {

if (melhorF < 0) {

// should never happen...

System.out.println("???????");

break;

}

for (int i = 0; i < f.length; i++) {

if (f[i] == melhorF && populacao.size() < tamanhoPopulacao) {

populacao.add((String) pop[i]);

}

}

melhorF--;

}

return melhor;

}



/**

* @param filho

* @return

*/

private static String mutate(String filho) {

int mp = randomico.nextInt(filho.length());

int mc = randomico.nextInt(filho.length()) + 1;

filho = filho.substring(0, mp) + mc

+ (mp + 1 == filho.length() ? "" : filho.substring(mp + 1));

return filho;

}



/**

* crossover

*

* @param p1

* @param p2

* @return

*/

private static String crossover(String p1, String p2) {

int i = randomico.nextInt(p1.length());

String ret = p1.substring(0, i) + p2.substring(i);

return ret;

}



/**

* seleciona um individuo da populacao aleatoriamente

*

* @param populacao

* @return

*/

private static String selecionarAleatorio(Set populacao, String px) {

String pn = px;

Object[] tmp = populacao.toArray();

while (pn.equals(px)) {

int i = randomico.nextInt((populacao.size()));

pn = (String) tmp[i];

}

return pn;

}



/**

* gerar um individuo com n posicoes

*

* @param n

* @return

*/

private static String gerarIndividuo(int n) {

String ret = "";

while (ret.length() < 8)

ret += (randomico.nextInt(n) + 1);

return ret;

}



/**

* função fitness, retorna a quantidade de rainhas a salvo.

*

* @param individuo

* @return

*/

public static int fitness(String individuo) {

int ret = 0;



int[][] tabuleiro = new int[8][8];

// primeiro representamos o tabuleiro com 0 e 1

for (int i = 0; i < 8; i++) {

int posicaoRainha = Integer.parseInt(individuo.substring(i, i + 1)) - 1;

for (int j = 0; j < 8; j++) {

tabuleiro[i][j] = posicaoRainha == j ? 1 : 0;

}

}



// agora verificamos quantas rainhas estao a salvo, este será o nosso

// retorno da função fitness

for (int i = 0; i < 8; i++) {

for (int j = 0; j < 8; j++) {

if (tabuleiro[i][j] == 1) {

if (!temAtacante(tabuleiro, i, j)) {

ret++;

}

}

}

}

return ret;

}



/**

* verifica se existe uma rainha ameaçando a posicao i,j existindo retorna

* true caso contrário retorna false

*

* @param tabuleiro

* @param i

* @param j

* @return

*/

private static boolean temAtacante(int[][] tabuleiro, int i, int j) {

// verificar na horizontal

for (int k = 0; k < 8; k++) {

if (k != i && tabuleiro[k][j] == 1)

return true;

}

// verificar na vertical

for (int k = 0; k < 8; k++) {

if (k != j && tabuleiro[i][k] == 1)

return true;

}

// verificar na diagonal1

int i0 = i - 1;

int j0 = j - 1;

while (i0 >= 0 && j0 >= 0) {

if (tabuleiro[i0][j0] == 1)

return true;

i0--;

j0--;

}

// verificar na diagonal2

i0 = i + 1;

j0 = j + 1;

while (i0 < 8 && j0 < 8) {

if (tabuleiro[i0][j0] == 1)

return true;

i0++;

j0++;

}

// verificar na diagonal3

i0 = i + 1;

j0 = j - 1;

while (i0 < 8 && j0 >= 0) {

if (tabuleiro[i0][j0] == 1)

return true;

i0++;

j0--;

}

// verificar na diagonal4

i0 = i - 1;

j0 = j + 1;

while (i0 >= 0 && j0 < 8) {

if (tabuleiro[i0][j0] == 1)

return true;

i0--;

j0++;

}

return false; // esta a salvo

}



}



sexta-feira, 18 de julho de 2008

Google Code Jam - Train Timetable

from Google Code Jam - july 2008....

Problem

A train line has two stations on it, A and B. Trains can take trips from A to B or from B to A multiple times during a day. When a train arrives at B from A (or arrives at A from B), it needs a certain amount of time before it is ready to take the return journey - this is the turnaround time. For example, if a train arrives at 12:00 and the turnaround time is 0 minutes, it can leave immediately, at 12:00.

A train timetable specifies departure and arrival time of all trips between A and B. The train company needs to know how many trains have to start the day at A and B in order to make the timetable work: whenever a train is supposed to leave A or B, there must actually be one there ready to go. There are passing sections on the track, so trains don't necessarily arrive in the same order that they leave. Trains may not travel on trips that do not appear on the schedule.

Input

The first line of input gives the number of cases, N. N test cases follow.

Each case contains a number of lines. The first line is the turnaround time, T, in minutes. The next line has two numbers on it, NA and NB. NA is the number of trips from A to B, and NB is the number of trips from B to A. Then there are NA lines giving the details of the trips from A to B.

Each line contains two fields, giving the HH:MM departure and arrival time for that trip. The departure time for each trip will be earlier than the arrival time. All arrivals and departures occur on the same day. The trips may appear in any order - they are not necessarily sorted by time. The hour and minute values are both two digits, zero-padded, and are on a 24-hour clock (00:00 through 23:59).

After these NA lines, there are NB lines giving the departure and arrival times for the trips from B to A.

Output

For each test case, output one line containing "Case #x: " followed by the number of trains that must start at A and the number of trains that must start at B.

Limits

1 ≤ N ≤ 100

Small dataset

0 ≤ NA, NB ≤ 20

0 ≤ T ≤ 5

Large dataset

0 ≤ NA, NB ≤ 100

0 ≤ T ≤ 60

Sample


Input

Output
2
5
3 2
09:00 12:00
10:00 13:00
11:00 12:30
12:02 15:00
09:00 10:30
2
2 0
09:00 09:01
12:00 12:02

Case #1: 2 2
Case #2: 2 0


SOLUTION

/**
sample file

2
5
3 2
09:00 12:00
10:00 13:00
11:00 12:30
12:02 15:00
09:00 10:30
2
2 0
09:00 09:01
12:00 12:02

*/
package codejam;

import java.io.File;
import java.io.FileInputStream;
import java.util.PriorityQueue;

/**
* @author Ricardo A. Harari - ricardo.harari@gmail.com
*
*/
public class TrainTimable {

PriorityQueue schedules = new PriorityQueue();

int turnaround = -1; // minutes

int n = 0;

private static final String FILE_NAME = "/tmp/sample.txt";

/**
* pass the full location of the sample file as argument
* if you dont pass anything it will search at /tmp/sample.txt
*
* @param args
*/
public static void main(String[] args) {
String fname = args.length == 0 ? FILE_NAME : args[0];
try {
TrainTimable o = new TrainTimable();
o.start(fname);
} catch (Exception e) {
System.out.println("Oppps.");
System.out.println("An exception has been throw:" + e.getMessage());
e.printStackTrace();
}
}

private void start(String fileName) throws Exception {
File f = new File(fileName); // sample file

int na = 0;
int nb = 0;

if (!f.exists() || !f.isFile()) throw new Exception("Sorry, " + fileName + " is not a valid file.");
FileInputStream fi = new FileInputStream(f);
int cint;
String tmp = new String();
int caseNum = 0;
int step = 0;
while ((cint = fi.read()) > -1) {
char c = (char) cint;
if (c == '\r' || c == '\n') {
tmp = tmp.trim();
if (tmp.length() > 0) {
if (step == 0) {
try {
n = Integer.parseInt(tmp);
step++;
} catch (NumberFormatException nfe) {
// do nothing. the 1st line should not be in correct format
}
} else if (step == 1) {
turnaround = Integer.parseInt(tmp);
caseNum++;
na = 0;
nb = 0;
schedules.clear();
step++;
} else if (step == 2) {
int pos = tmp.lastIndexOf(' ');
if (pos > -1) {
na = Integer.parseInt(tmp.substring(0, pos).trim());
nb = Integer.parseInt(tmp.substring(pos).trim());
}
step++;
} else if (na > 0) {
addToSchedule(tmp, true);
na--;
} else if (nb > 0) {
addToSchedule(tmp, false);
nb--;
}
if (step == 3 && na == 0 && nb == 0) {
process(caseNum);
turnaround = 0;
step = 1;
}
tmp = "";
}
} else {
tmp += c;
}
}
if (step == 3) {
process(caseNum);
}
fi.close();
}

/**
* @param caseNum
*/
private void process(int caseNum) {
int trainA = 0;
int trainB = 0;
int availableTrainA = 0;
int availableTrainB = 0;

while (schedules.size() > 0) {
Schedule sched = schedules.poll();
if (!sched.arrive) {
boolean isFromA = sched.source.equals("A");
if (isFromA) {
if (availableTrainA > 0) {
availableTrainA--;
} else {
trainA++;
}
} else {
if (availableTrainB > 0) {
availableTrainB--;
} else {
trainB++;
}
}
} else {
boolean isToB = sched.source.equals("B");
if (isToB) {
availableTrainB++;
} else {
availableTrainA++;
}
}
}
print(caseNum, trainA, trainB);
}

private long hourToLong(String time) {
int hour = Integer.parseInt(time.substring(0, 2));
int minute = Integer.parseInt(time.substring(3));
return hour*60 + minute;
}

/**
* @param tmp
* @param b
*/
private void addToSchedule(String tmp, boolean fromA) {
int pos = tmp.lastIndexOf(' ');
if (pos > -1) {
long start = hourToLong(tmp.substring(0, pos).trim());
long end = hourToLong(tmp.substring(pos).trim()) + turnaround;
schedules.add(new Schedule(start * 100000 + end, fromA ? "A" : "B", false));
schedules.add(new Schedule(end * 100000, fromA ? "B" : "A", true));
}
}

/**
* @param caso
* @param i
*/
private void print(int caso, int na, int nb) {
System.out.println("Case #" + caso + ": " + na + " " + nb);
}

/**
*
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
class Schedule implements Comparable {

public Schedule(long _nextSchedule, String _source, boolean _arrive) {
nextSchedule = _nextSchedule;
source = _source;
arrive = _arrive;
}
long nextSchedule;
String source;
boolean arrive = false;

/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Schedule o) {
if (nextSchedule - o.nextSchedule < 0) return -1;
if (nextSchedule - o.nextSchedule > 0) return 1;
return 0;
}
}

}

Google Code Jam - Saving the Universe

from Google Code Jam - july/2008....
http://code.google.com/codejam

Problem

The urban legend goes that if you go to the Google homepage and search for "Google", the universe will implode. We have a secret to share... It is true! Please don't try it, or tell anyone. All right, maybe not. We are just kidding.

The same is not true for a universe far far away. In that universe, if you search on any search engine for that search engine's name, the universe does implode!

To combat this, people came up with an interesting solution. All queries are pooled together. They are passed to a central system that decides which query goes to which search engine. The central system sends a series of queries to one search engine, and can switch to another at any time. Queries must be processed in the order they're received. The central system must never send a query to a search engine whose name matches the query. In order to reduce costs, the number of switches should be minimized.

Your task is to tell us how many times the central system will have to switch between search engines, assuming that we program it optimally.

Input

The first line of the input file contains the number of cases, N. N test cases follow.

Each case starts with the number S -- the number of search engines. The next S lines each contain the name of a search engine. Each search engine name is no more than one hundred characters long and contains only uppercase letters, lowercase letters, spaces, and numbers. There will not be two search engines with the same name.

The following line contains a number Q -- the number of incoming queries. The next Q lines will each contain a query. Each query will be the name of a search engine in the case.

Output

For each input case, you should output:

Case #X: Y
where X is the number of the test case and Y is the number of search engine switches. Do not count the initial choice of a search engine as a switch.

Limits

0 < N ≤ 20

Small dataset

2 ≤ S ≤ 10

0 ≤ Q ≤ 100

Large dataset

2 ≤ S ≤ 100

0 ≤ Q ≤ 1000

Sample


Input

Output
2
5
Yeehaw
NSM
Dont Ask
B9
Googol
10
Yeehaw
Yeehaw
Googol
B9
Googol
NSM
B9
NSM
Dont Ask
Googol
5
Yeehaw
NSM
Dont Ask
B9
Googol
7
Googol
Dont Ask
NSM
NSM
Yeehaw
Yeehaw
Googol

Case #1: 1
Case #2: 0

In the first case, one possible solution is to start by using Dont Ask, and switch to NSM after query number 8.
For the second case, you can use B9, and not need to make any switches.


SOLUTION

/**
*
*/
package codejam;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;

/**
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
public class SavingUniverse {

private static final String FILE_NAME = "/tmp/sample.txt";


/**
* pass the full location of the sample file as argument
* if you dont pass anything it will search at /tmp/sample.txt
*
* @param args
*/
public static void main(String[] args) {
String fname = args.length == 0 ? FILE_NAME : args[0];
try {
SavingUniverse o = new SavingUniverse();
o.start(fname);
} catch (Exception e) {
System.out.println("Oppps.");
System.out.println("An exception has been throw:" + e.getMessage());
e.printStackTrace();
}
}

private int n = 0; // # of cases
ArrayList listEngines = null; // search engines nodes
ArrayList listQueries = null;

private void start(String fileName) throws Exception {
File f = new File(fileName); // sample file
int s = 0; // # of search engines
int q = 0; // # of queries

if (!f.exists() || !f.isFile()) throw new Exception("Sorry, " + fileName + " is not a valid file.");
FileInputStream fi = new FileInputStream(f);
int cint;
String tmp = new String();
int caseNum = 0;
while ((cint = fi.read()) > -1) {
char c = (char) cint;
if (c == '\r' || c == '\n') {
if (tmp.length() > 0) {
if (n == 0) {
try {
n = Integer.parseInt(tmp);
} catch (NumberFormatException nfe) {
// do nothing. the 1st line should not be in correct format
}
} else if (q >0) {
addQuery(tmp);
q--;
if (q == 0) {
process(caseNum);
s = 0;
}
} else if (s == 0) {
s = Integer.parseInt(tmp);
if (listEngines == null){
listEngines = new ArrayList();
listQueries = new ArrayList();
} else {
listEngines.clear();
listQueries.clear();
}
caseNum++;
} else if (s > 0) {
listEngines.add(new SEngineNode(tmp));
s--;
if (s == 0) s = -1;
} else if (s == -1) {
q = Integer.parseInt(tmp);
if (q == 0) { // no queries huh?
print(caseNum, 0);
s = 0;
}
}
tmp = "";
}
} else if (c != ' ') {
tmp += c;
}
}
if (n != 0 && q > 0) {
addQuery(tmp.toString());
q--;
if (q == 0) {
process(caseNum);
}
}
}

/**
*
*/
private void process(int caso) {
// check if the 1st node has value of 0
int counter = 0;
for (int i = 0; i <>
QueryNode qrynode = listQueries.get(i);
SEngineNode engine = qrynode.node;
ArrayList farNodeList = cloneEngines(engine);
int j = i + 1;
for (j = i; j <>
QueryNode qrynodenxt = listQueries.get(j);
removeEngine(farNodeList, qrynodenxt);
if (farNodeList.size() == 0) break;
}
if (farNodeList.size() == 0) {
counter++;
i = j - 1;
} else {
break;
}
}
print(caso, counter);
}

/**
* @param farNodeList
* @param qrynodenxt
*/
private void removeEngine(ArrayList farNodeList, QueryNode qrynodenxt) {
String s = qrynodenxt.query;
for (SEngineNode n : farNodeList) {
if (s.indexOf(n.engineName) > -1) {
farNodeList.remove(n);
break;
}
}
}

/**
* @return
*/
private ArrayList cloneEngines(SEngineNode exceptNode) {
ArrayList ret = new ArrayList();
for (SEngineNode node : listEngines) {
if (!node.engineName.equals(exceptNode.engineName)) ret.add(node);
}
return ret;
}

/**
* @param caso
* @param i
*/
private void print(int caso, int i) {
System.out.println("Case #" + caso + ": " + i);
}

/**
* @param string
*/
private void addQuery(String s) {
s = s.toUpperCase();
for (SEngineNode node : listEngines) {
if (s.indexOf(node.engineName) > -1) {
node.qtd++;
listQueries.add(new QueryNode(s, node));
break;
}
}
}

class QueryNode {
public QueryNode(String _query, SEngineNode _engine) {
query = _query;
node = _engine;
}
String query;
SEngineNode node;
}

class SEngineNode {
public SEngineNode(String _engineName) {
engineName = _engineName.toUpperCase().trim();
}
int qtd; // qtd that match this search engine
String engineName;
}

}