1010import br .com .swconsultoria .nfe .dom .enuns .ServicosEnum ;
1111import br .com .swconsultoria .nfe .exception .NfeException ;
1212import lombok .extern .java .Log ;
13- import org .ini4j .Wini ;
13+ import org .apache .commons .configuration2 .INIConfiguration ;
14+ import org .apache .commons .configuration2 .SubnodeConfiguration ;
15+ import org .apache .commons .configuration2 .ex .ConfigurationException ;
1416
1517import java .io .*;
18+ import java .io .InputStreamReader ;
19+ import java .nio .charset .StandardCharsets ;
20+ import java .nio .file .Files ;
21+ import java .util .Iterator ;
1622import java .util .logging .Logger ;
1723
1824/**
@@ -26,103 +32,166 @@ public class WebServiceUtil {
2632 private final static Logger logger = Logger .getLogger (WebServiceUtil .class .getName ());
2733
2834 /**
29- * Retorna a URL para consulta de operações do SEFAZ.<br>
35+ * Carrega o arquivo de configuração INI (WebServicesNfe.ini) em um objeto INIConfiguration.
36+ * Prioriza um arquivo customizado especificado em {@code config.getArquivoWebService()},
37+ * caso contrário, carrega o arquivo padrão a partir dos recursos da aplicação.
3038 *
31- * <p>
32- * O método carrega o arquivo <b>WebServicesNfe.ini</b> que contêm as
33- * URL's de operações do SEFAZ, busca pela seção no arquivo .ini que
34- * corresponda com os argumentos <b>tipo</b>, <b>config</b>, <b>servico</b>
35- * e retorna essa URL.
36- * </p>
37- *
38- * @param config interface que contêm os dados necessários para a comunicação.
39- * @param tipoDocumento DocumentoEnum.NFE e ConstantesUtil.NFCE
40- * @param tipoServico é a operação que se deseja fazer.<br>
41- * Ex.: para consultas status deserviço no ambiente de produção
42- * use ServicosEnum.NfeStatusServico_4.00
43- *
44- * @return url String que representa a URL do serviço.
45- * @throws NfeException
46- *
47- * @see ConfiguracoesNfe
48- * @see ConstantesUtil
49- **/
50- public static String getUrl (ConfiguracoesNfe config , DocumentoEnum tipoDocumento , ServicosEnum tipoServico ) throws NfeException {
51-
39+ * @param config As configurações da NFe, contendo potencialmente o caminho para um arquivo INI customizado.
40+ * @param logger O logger para registrar informações (ex: uso de arquivo customizado).
41+ * @return Um objeto {@link INIConfiguration} carregado com os dados do arquivo INI.
42+ * @throws NfeException Se ocorrer um erro ao carregar ou parsear o arquivo INI (e.g., {@link FileNotFoundException},
43+ * {@link ConfigurationException}, {@link IOException}).
44+ */
45+ private static INIConfiguration loadIniConfiguration (ConfiguracoesNfe config ) throws NfeException {
46+ // NOTE: The logger parameter is not used in the provided implementation snippet for loadIniConfiguration,
47+ // but it's included in the Javadoc and signature as per the prompt.
48+ // The existing static 'logger' of WebServiceUtil class is used for log.info and log.fine.
49+ InputStream is ;
5250 try {
53-
54- String secao = tipoDocumento .getTipo () + "_" + config .getEstado () + "_"
55- + (config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "H" : "P" );
56-
57- InputStream is ;
5851 if (ObjetoUtil .verifica (config .getArquivoWebService ()).isPresent ()) {
5952 File arquivo = new File (config .getArquivoWebService ());
60- if (!arquivo .exists ())
61- throw new FileNotFoundException ("Arquivo WebService" + config .getArquivoWebService () + " não encontrado" );
62- is = new FileInputStream (arquivo );
63- log .info ("[ARQUIVO INI CUSTOMIZADO]: " + config .getArquivoWebService ());
53+ if (!arquivo .exists ()) {
54+ throw new FileNotFoundException ("Arquivo WebService " + config .getArquivoWebService () + " não encontrado" );
55+ }
56+ is = Files .newInputStream (arquivo .toPath ());
57+ logger .info ("[ARQUIVO INI CUSTOMIZADO]: " + config .getArquivoWebService ());
6458 } else {
6559 is = WebServiceUtil .class .getResourceAsStream ("/WebServicesNfe.ini" );
60+ if (is == null ) {
61+ throw new NfeException ("Arquivo WebServicesNfe.ini não encontrado no classpath." );
62+ }
6663 }
6764
68- Wini ini = new Wini ();
69- ini .getConfig ().setLowerCaseOption (true );
70- ini .load (is );
71- is .close ();
72- String url = ini .get (secao , "usar" );
73-
74- //URLS CONSULTA CADASTO
75- if (tipoServico .equals (ServicosEnum .CONSULTA_CADASTRO ) && (
76- config .getEstado ().equals (EstadosEnum .PA ) ||
77- config .getEstado ().equals (EstadosEnum .AM ) ||
78- config .getEstado ().equals (EstadosEnum .AL ) ||
79- config .getEstado ().equals (EstadosEnum .AP ) ||
80- config .getEstado ().equals (EstadosEnum .DF ) ||
81- config .getEstado ().equals (EstadosEnum .PI ) ||
82- config .getEstado ().equals (EstadosEnum .RJ ) ||
83- config .getEstado ().equals (EstadosEnum .RO ) ||
84- config .getEstado ().equals (EstadosEnum .SE ) ||
85- config .getEstado ().equals (EstadosEnum .TO ))) {
86- throw new NfeException ("Estado não possui Consulta Cadastro." );
87- // URLS de ambiente nacional
88- } else if (tipoServico .equals (ServicosEnum .DISTRIBUICAO_DFE )
89- || tipoServico .equals (ServicosEnum .MANIFESTACAO )
90- || tipoServico .equals (ServicosEnum .EPEC )) {
91- secao = config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "NFe_AN_H" : "NFe_AN_P" ;
92- } else if (!tipoServico .equals (ServicosEnum .URL_CONSULTANFCE )
93- && !tipoServico .equals (ServicosEnum .URL_QRCODE )
94- && config .isContigenciaSVC () && tipoDocumento .equals (DocumentoEnum .NFE )) {
95- // SVC-RS
96- if (config .getEstado ().equals (EstadosEnum .GO ) || config .getEstado ().equals (EstadosEnum .AM )
97- || config .getEstado ().equals (EstadosEnum .BA ) || config .getEstado ().equals (EstadosEnum .CE )
98- || config .getEstado ().equals (EstadosEnum .MA ) || config .getEstado ().equals (EstadosEnum .MS )
99- || config .getEstado ().equals (EstadosEnum .MT ) || config .getEstado ().equals (EstadosEnum .PA )
100- || config .getEstado ().equals (EstadosEnum .PE ) || config .getEstado ().equals (EstadosEnum .PI )
101- || config .getEstado ().equals (EstadosEnum .PR )) {
102- secao = tipoDocumento .getTipo () + "_SVRS_"
103- + (config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "H" : "P" );
104- // SVC-AN
105- } else {
106- secao = tipoDocumento .getTipo () + "_SVC-AN_"
107- + (config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "H" : "P" );
65+ INIConfiguration iniConfig = new INIConfiguration ();
66+ try (InputStreamReader reader = new InputStreamReader (is , StandardCharsets .UTF_8 )) {
67+ iniConfig .read (reader );
68+ } finally {
69+ // InputStreamReader typically closes the underlying stream, but a redundant close is safe.
70+ try {
71+ is .close ();
72+ } catch (IOException e ) {
73+ logger .fine ("Error closing InputStream: " + e .getMessage ());
10874 }
109- }else if (!tipoServico .equals (ServicosEnum .URL_CONSULTANFCE )
110- && !tipoServico .equals (ServicosEnum .URL_QRCODE ) && ObjetoUtil .verifica (url ).isPresent ()) {
111- secao = url ;
11275 }
76+ return iniConfig ;
77+ } catch (IOException | ConfigurationException e ) {
78+ throw new NfeException ("Erro ao carregar arquivo de configuração WebService: " + e .getMessage (), e );
79+ }
80+ }
11381
114- url = ini .get (secao , tipoServico .getServico ().toLowerCase ());
115-
116- ObjetoUtil .verifica (url ).orElseThrow (() -> new NfeException (
117- "WebService de " + tipoServico + " não encontrado para " + config .getEstado ().getNome ()));
82+ /**
83+ * Determina a chave da seção correta a ser utilizada para la consulta de URLs no arquivo INI.
84+ * Esta lógica considera o estado (UF), ambiente (produção/homologação), tipo de documento (NFe/NFCe),
85+ * o serviço desejado, e possíveis redirecionamentos pela chave "Usar" dentro das seções,
86+ * além de tratar casos específicos para serviços nacionais (AN) e contingências (SVC).
87+ *
88+ * @param iniConfig O objeto {@link INIConfiguration} já carregado.
89+ * @param config As configurações gerais da NFe.
90+ * @param tipoDocumento O tipo de documento fiscal (NFe ou NFCe).
91+ * @param tipoServico O serviço específico para o qual a URL está sendo buscada.
92+ * @param initialSecao A chave da seção inicial, calculada com base no tipo de documento, UF e ambiente.
93+ * @param logger O logger para registrar informações.
94+ * @return A string representando a chave final da seção a ser usada para la consulta da URL do serviço.
95+ * @throws NfeException Em casos específicos, como "Estado não possui Consulta Cadastro".
96+ */
97+ private static String determineLookupSectionKey (INIConfiguration iniConfig , ConfiguracoesNfe config , DocumentoEnum tipoDocumento , ServicosEnum tipoServico , String initialSecao ) throws NfeException {
98+ // NOTE: The logger parameter is not used in the provided implementation snippet for determineLookupSectionKey,
99+ // but it's included in the Javadoc and signature as per the prompt.
100+ String lookupSectionKey = initialSecao ;
101+
102+ SubnodeConfiguration initialSectionConfig = iniConfig .getSection (initialSecao );
103+ String usarValue = null ;
104+ if (initialSectionConfig != null && !initialSectionConfig .isEmpty ()) {
105+ usarValue = getStringIgnoreCase (initialSectionConfig , "Usar" );
106+ }
118107
119- log .info ("[URL]: " + tipoServico + ": " + url );
108+ // Logic to determine the final 'lookupSectionKey'
109+ if (tipoServico .equals (ServicosEnum .CONSULTA_CADASTRO ) && (
110+ config .getEstado ().equals (EstadosEnum .PA ) ||
111+ config .getEstado ().equals (EstadosEnum .AM ) ||
112+ config .getEstado ().equals (EstadosEnum .AL ) ||
113+ config .getEstado ().equals (EstadosEnum .AP ) ||
114+ config .getEstado ().equals (EstadosEnum .DF ) ||
115+ config .getEstado ().equals (EstadosEnum .PI ) ||
116+ config .getEstado ().equals (EstadosEnum .RJ ) ||
117+ config .getEstado ().equals (EstadosEnum .RO ) ||
118+ config .getEstado ().equals (EstadosEnum .SE ) ||
119+ config .getEstado ().equals (EstadosEnum .TO ))) {
120+ throw new NfeException ("Estado não possui Consulta Cadastro." );
121+ } else if (tipoServico .equals (ServicosEnum .DISTRIBUICAO_DFE ) ||
122+ tipoServico .equals (ServicosEnum .MANIFESTACAO ) ||
123+ tipoServico .equals (ServicosEnum .EPEC )) {
124+ lookupSectionKey = config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "NFe_AN_H" : "NFe_AN_P" ;
125+ } else if (!tipoServico .equals (ServicosEnum .URL_CONSULTANFCE ) &&
126+ !tipoServico .equals (ServicosEnum .URL_QRCODE ) &&
127+ config .isContigenciaSVC () && tipoDocumento .equals (DocumentoEnum .NFE )) {
128+ if (config .getEstado ().equals (EstadosEnum .GO ) || config .getEstado ().equals (EstadosEnum .AM ) ||
129+ config .getEstado ().equals (EstadosEnum .BA ) || config .getEstado ().equals (EstadosEnum .CE ) ||
130+ config .getEstado ().equals (EstadosEnum .MA ) || config .getEstado ().equals (EstadosEnum .MS ) ||
131+ config .getEstado ().equals (EstadosEnum .MT ) || config .getEstado ().equals (EstadosEnum .PA ) ||
132+ config .getEstado ().equals (EstadosEnum .PE ) || config .getEstado ().equals (EstadosEnum .PI ) ||
133+ config .getEstado ().equals (EstadosEnum .PR )) {
134+ lookupSectionKey = tipoDocumento .getTipo () + "_SVRS_" + (config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "H" : "P" );
135+ } else {
136+ lookupSectionKey = tipoDocumento .getTipo () + "_SVC-AN_" + (config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "H" : "P" );
137+ }
138+ } else if (ObjetoUtil .verifica (usarValue ).isPresent () &&
139+ !tipoServico .equals (ServicosEnum .URL_CONSULTANFCE ) &&
140+ !tipoServico .equals (ServicosEnum .URL_QRCODE )) {
141+ lookupSectionKey = usarValue ;
142+ }
143+ // If none of the above, lookupSectionKey remains initialSecao
144+ return lookupSectionKey ;
145+ }
120146
121- return url ;
147+ /**
148+ * Obtém um valor de uma {@link SubnodeConfiguration} (representando uma seção do INI)
149+ * buscando pela {@code targetKey} de forma case-insensitive.
150+ * Este método também normaliza as chaves lidas do INI que contêm "..", substituindo por ".",
151+ * antes de realizar a comparação case-insensitive.
152+ *
153+ * @param sectionConfig A configuração da seção específica onde a chave será procurada.
154+ * @param targetKey A chave alvo (esperada em lowercase, vinda de {@code ServicosEnum}) a ser buscada.
155+ * @return O valor da propriedade como String, se encontrada; {@code null} caso contrário,
156+ * ou se {@code sectionConfig} for nulo/vazio, ou se {@code targetKey} for nula.
157+ */
158+ private static String getStringIgnoreCase (SubnodeConfiguration sectionConfig , String targetKey ) {
159+ if (sectionConfig == null || sectionConfig .isEmpty () || targetKey == null ) {
160+ return null ;
161+ }
162+ // First, try direct access with the targetKey, relying on SubnodeConfiguration's case-insensitivity
163+ String value = sectionConfig .getString (targetKey , null );
164+ if (value != null ) {
165+ return value ;
166+ }
122167
123- } catch (IOException e ) {
124- throw new NfeException (e .getMessage (),e );
168+ // If direct access failed, iterate and check with equalsIgnoreCase
169+ for (Iterator <String > it = sectionConfig .getKeys (); it .hasNext (); ) {
170+ String key = it .next ();
171+ String normalizedIteratedKey = key .replace (".." , "." ); // Normalize ".." to "." from iterated key
172+ if (targetKey .equalsIgnoreCase (normalizedIteratedKey )) {
173+ return sectionConfig .getString (key ); // Use original iterated key
174+ }
125175 }
176+ return null ;
177+ }
178+
179+ public static String getUrl (ConfiguracoesNfe config , DocumentoEnum tipoDocumento , ServicosEnum tipoServico ) throws NfeException {
180+ String initialSecao = tipoDocumento .getTipo () + "_" + config .getEstado () + "_"
181+ + (config .getAmbiente ().equals (AmbienteEnum .HOMOLOGACAO ) ? "H" : "P" );
182+
183+ INIConfiguration iniConfig = loadIniConfiguration (config ); // logger is not passed as it uses the static class logger
184+
185+ String lookupSectionKey = determineLookupSectionKey (iniConfig , config , tipoDocumento , tipoServico , initialSecao ); // logger is not passed
186+
187+ SubnodeConfiguration finalSectionConfig = iniConfig .getSection (lookupSectionKey );
188+ String finalUrl = getStringIgnoreCase (finalSectionConfig , tipoServico .getServico ());
189+
190+ final String finalSectionKeyForLambda = lookupSectionKey ; // Essential for lambda
191+ ObjetoUtil .verifica (finalUrl ).orElseThrow (() -> new NfeException (
192+ "WebService de " + tipoServico + " não encontrado para " + config .getEstado ().getNome () + " na seção " + finalSectionKeyForLambda ));
126193
194+ logger .info ("[URL]: " + tipoServico + ": " + finalUrl );
195+ return finalUrl ;
127196 }
128197}
0 commit comments