org.jrimum.texgit.Field Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bpp-cobranca Show documentation
Show all versions of bpp-cobranca Show documentation
This is a fork and merge from JRimum ( http://www.jrimum.org ),
- Bopepo: https://github.com/jrimum/bopepo
- Texgit: https://github.com/jrimum/texgit
- Valia: https://github.com/jrimum/vallia
- Utilix: https://github.com/jrimum/utilix
- Domkee: https://github.com/jrimum/domkee
For Brazillian Boleto Payment Method. So much thanks for original authors:
Gilmar P. S. L, Misael Barreto and Rômulo Augusto.
The newest version!
/*
* Copyright 2008 JRimum Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
* applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
* Created at: 26/07/2008 - 12:44:41
*
* ================================================================================
*
* Direitos autorais 2008 JRimum Project
*
* Licenciado sob a Licença Apache, Versão 2.0 ("LICENÇA"); você não pode usar
* esse arquivo exceto em conformidade com a esta LICENÇA. Você pode obter uma
* cópia desta LICENÇA em http://www.apache.org/licenses/LICENSE-2.0 A menos que
* haja exigência legal ou acordo por escrito, a distribuição de software sob
* esta LICENÇA se dará “COMO ESTÁ”, SEM GARANTIAS OU CONDIÇÕES DE QUALQUER
* TIPO, sejam expressas ou tácitas. Veja a LICENÇA para a redação específica a
* reger permissões e limitações sob esta LICENÇA.
*
* Criado em: 26/07/2008 - 12:44:41
*
*/
package org.jrimum.texgit;
import static java.lang.String.format;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNumeric;
import org.jrimum.utilix.Dates;
import static org.jrimum.utilix.ObjectUtil.isNotNull;
import org.jrimum.utilix.Objects;
import org.jrimum.utilix.StringUtil;
/**
* @author Gilmar P.S.L.
*
* @param
*/
@SuppressWarnings("serial")
public class Field implements org.jrimum.texgit.IField, TextStream {
/**
*
* Nome do campo, também pode ser usado como id.
*
*/
protected String name;
/**
*
*/
protected Integer length;
/**
*
* Valor do campo.
*
*/
private G value;
/**
*
* Formatador utilizado na leitura e escrita do valor do campo.
*
*/
private Format formatter;
/**
* Preenchedor do value utilizado na hora da escrita.
*/
protected IFiller filler;
/**
*
* Necessário para ler campos númericos em branco.
*
*/
private boolean blankAccepted;
/**
*
* Ao ultrapassar o tamanho, define se pode truncar ou se dispara uma
* exceção.
*
*/
protected boolean truncate;
/**
*
* Quando definido, todos os caracteres não numericos são desprezados/removidos
*
*/
protected boolean apenasDigitos;
/**
*
*/
public Field() {
super();
}
/**
* @param value
*/
public Field(G value) {
super();
setValue(value);
}
/**
*
* Cria um Field
com um valor e um formatador para o valor.
* Isto significa que a leitura e escrita do valor informado será de acordo
* com o formatador.
*
*
* @param value
* @param formatter
*/
public Field(G value, Format formatter) {
setValue(value);
setFormatter(formatter);
}
/**
* @param name
* @param value
*/
public Field(String name, G value) {
super();
setName(name);
setValue(value);
}
/**
*
* Cria um Field
com nome para identificação, valor e um
* formatador.
*
*
* @param name
* @param value
* @param formatter
*
* @see #Field(Object, Format)
*/
public Field(String name, G value, Format formatter) {
setName(name);
setValue(value);
setFormatter(formatter);
}
@SuppressWarnings("unchecked")
@Override
public Field clone() throws CloneNotSupportedException {
return (Field) super.clone();
}
//Problema: https://stackoverflow.com/questions/3403909/get-generic-type-of-class-at-runtime
//
public void read(String str) {
Objects.checkNotNull(str, "String inválida [null]!");
if (str.length() != length) {
throw new IllegalArgumentException("O tamanho da String [ "
+ str + " ] é incompatível com o especificado [ " + length + " ]!");
}
//Tentar inferir o tipo generico do Field
//Melhorar isso, pois todas as formas são problematicas
//https://stackoverflow.com/questions/3403909/get-generic-type-of-class-at-runtime
//O ideal seria fixar uma campo com o valor da Class generica.
Class> valueType = String.class;//Tipo padrão
try {
if (value != null) {
valueType = value.getClass();
} else {
Class> tmpValueType = getGenericTypeArgument(this.getClass(), 0);
if (tmpValueType != null) {
valueType = tmpValueType;
}
}
} catch (Exception e) {
}
try {
if (this.value instanceof TextStream
|| TextStream.class.isAssignableFrom(valueType)) {
TextStream reader = (TextStream) this.value;
reader.read(str);
} else if (this.value instanceof BigDecimal
|| BigDecimal.class.isAssignableFrom(valueType)) {
readDecimalField(str);
} else if (this.value instanceof Date
|| Date.class.isAssignableFrom(valueType)) {
readDateField(str);
} else if (this.value instanceof Character
|| Character.class.isAssignableFrom(valueType)) {
readCharacter(str);
} else if (this.value instanceof Number
|| Number.class.isAssignableFrom(valueType)) {
readNumeric(valueType, str);
} else {
readStringOrNumericField(str);
}
} catch (Exception e) {
throw new IllegalStateException(format("Falha na leitura do campo! %s", toString()), e);
}
}
@SuppressWarnings("unchecked")
private void readCharacter(String str) {
if (str.length() == 1) {
value = (G) new Character(str.charAt(0));
} else {
throw new IllegalArgumentException("String com mais de 1 character!");
}
}
@SuppressWarnings("unchecked")
private void readDecimalField(String str) {
DecimalFormat decimalFormat = (DecimalFormat) formatter;
try {
String number = parseNumber(str);
Long parsedValue = (Long) formatter.parseObject(number);
BigDecimal decimalValue = new BigDecimal(parsedValue.longValue());
decimalValue = decimalValue.movePointLeft(decimalFormat.getMaximumFractionDigits());
value = (G) decimalValue;
} catch (ParseException e) {
throwReadError(e, str);
}
}
@SuppressWarnings("unchecked")
private void readDateField(String str) {
try {
if (isBlank(str)) {
if (isBlankAccepted()) {
value = (G) Dates.invalidDate();
} else {
new IllegalArgumentException(format("Campo data vazio não permitido: [%s]!", str));
}
} else {
value = (G) formatter.parseObject(str);
}
} catch (ParseException e) {
throwReadError(e, str);
}
}
@SuppressWarnings("unchecked")
private void readStringOrNumericField(String str) {
str = parseNumber(str);
// if (value != null && value.getClass().equals(String.class)) {
value = (G) str;
// } else {
// readNumeric(clazz, str);
// }
}
public static Class getGenericTypeArgument(final Class> clazz, final int idx) {
final Type type = clazz.getGenericSuperclass();
ParameterizedType paramType;
try {
paramType = (ParameterizedType) type;
} catch (ClassCastException cause) {
paramType = (ParameterizedType) ((Class) type).getGenericSuperclass();
}
return (Class) paramType.getActualTypeArguments()[idx];
}
@SuppressWarnings("unchecked")
private void readNumeric(Class> clazz, String str) {
for (Constructor> cons : clazz.getConstructors()) {
if (cons.getParameterTypes().length == 1) {
if (cons.getParameterTypes()[0].equals(String.class)) {
try {
value = (G) cons.newInstance(str);
} catch (Exception e) {
throwReadError(e, str);
}
}
}
}
}
protected String truncate(String str) {
if (truncate && length != null && str.length() > length) {
str = str.substring(0, length);
}
return str;
}
protected String fill(String str) {
if (length != null && isNotNull(filler)) {
str = filler.fill(str, length);
}
return str;
}
public String write() {
try {
String str = null;
if (value instanceof TextStream) {
TextStream its = (TextStream) value;
str = its.write();
} else if (value instanceof Date) {
str = writeDateField();
} else if (value instanceof Number) {
str = writeDecimalField();
} else {
str = value.toString();
}
if (this.apenasDigitos) {
str = StringUtil.eliminateSymbols(str);
}
str = fill(str);
if (length != null && str.length() != length) {
if (!truncate) {
throw new IllegalArgumentException("O campo [ " + str
+ " ] é incompatível com o especificado [" + length + "]!");
}
}
return StringUtil.eliminateAccent(str).toUpperCase();
} catch (Exception e) {
throw new IllegalStateException(format("Falha na escrita do campo escrita! %s", toString()), e);
}
}
private String writeDecimalField() {
String ret = "" + value;
if (value instanceof BigDecimal) {
BigDecimal decimalValue = (BigDecimal) value;
if (formatter == null) {
formatter = NumberFormat.getInstance();
}
decimalValue = decimalValue.movePointRight(((DecimalFormat) formatter).getMaximumFractionDigits());
ret = decimalValue.toString();
} else if (value instanceof Number && formatter != null) {
ret = formatter.format(value);
}
return ret;
}
private String writeDateField() {
if (!Dates.equalsInvalidDate((Date) value)) {
return formatter.format(value);
}
return EMPTY;
}
private String parseNumber(String str) {
if (isBlank(str)) {
if (isBlankAccepted()) {
str = "0";
} else {
new IllegalArgumentException(format("Campo numérico vazio não permitido: [%s]!", str));
}
} else if (!isNumeric(str)) {
new IllegalArgumentException(format("O campo deve ser numérico e não: [%s]!", str));
}
return str;
}
public String getName() {
return name;
}
public void setName(String name) {
if (isNotNull(name)) {
this.name = name;
} else {
throw new IllegalArgumentException(format("Nome Inválido: [%s]!", name));
}
}
public boolean isBlankAccepted() {
return this.blankAccepted;
}
public void setBlankAccepted(boolean blankAccepted) {
this.blankAccepted = blankAccepted;
}
public G getValue() {
return value;
}
public void setValue(G value) {
if (isNotNull(value)) {
this.value = value;
} else {
throw new IllegalArgumentException(format("Valor Inválido: [%s]!", value));
}
}
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
if (length > 0) {
this.length = length;
} else {
throw new IllegalArgumentException("Tamanho inválido [ " + length + " ]!");
}
}
public Format getFormatter() {
return formatter;
}
public void setFormatter(Format formatter) {
if (isNotNull(formatter)) {
this.formatter = formatter;
} else {
throw new IllegalArgumentException(format("Formato inválido: [%s]!", formatter));
}
}
public IFiller getFiller() {
return filler;
}
public void setFiller(Filler> filler) {
if (isNotNull(filler)) {
this.filler = filler;
} else {
throw new IllegalArgumentException("Filler inválido [ " + filler + " ]!");
}
}
private void throwReadError(Exception e, String value) {
throw new IllegalArgumentException(format("Falha na leitura da string: [\"%s\"]! %s", value, toString()), e);
}
@Override
public String toString() {
return format("Field [name=\"%s\", value=\"%s\", isBlankAccepted=%s, apenasDigitos=%s, formatter=%s]",
Objects.whenNull(this.name, EMPTY),
Objects.whenNull(this.value, EMPTY),
this.apenasDigitos,
Objects.whenNull(this.isBlankAccepted(), EMPTY),
Objects.whenNull(this.formatter, EMPTY));
}
}