com.webcohesion.ofx4j.client.impl.OFXHomeFIDataStore.java Source code

Java tutorial

Introduction

Here is the source code for com.webcohesion.ofx4j.client.impl.OFXHomeFIDataStore.java

Source

/*
 * Copyright 2008 Web Cohesion
 *
 * 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.
 */

package com.webcohesion.ofx4j.client.impl;

import com.webcohesion.ofx4j.client.FinancialInstitutionData;
import com.webcohesion.ofx4j.client.FinancialInstitutionDataStore;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ccil.cowan.tagsoup.Parser;
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 java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Implementation of a financial institution data store that reads its FI data from <a href="http://www.ofxhome.com">OFX Home</a>.
 *
 * @author Ryan Heaton
 */
public class OFXHomeFIDataStore implements FinancialInstitutionDataStore {

    private static final Log LOG = LogFactory.getLog(OFXHomeFIDataStore.class);
    public static final Pattern INSTITUTION_HREF_PATTERN = Pattern
            .compile("http\\://www.ofxhome.com/index.php/institution/view/\\w+");
    private String url = "http://www.ofxhome.com/index.php/home/directory/all";
    private Map<String, FinancialInstitutionData> fiData;

    public FinancialInstitutionData getInstitutionData(String fid) {
        return getFiData().get(fid);
    }

    public List<FinancialInstitutionData> getInstitutionDataList() {
        return new ArrayList<FinancialInstitutionData>(getFiData().values());
    }

    public synchronized Map<String, FinancialInstitutionData> getFiData() {
        if (this.fiData == null) {
            this.fiData = new HashMap<String, FinancialInstitutionData>();
            try {
                initializeFIData();
                if (LOG.isInfoEnabled()) {
                    LOG.info(this.fiData.size() + " institutions loaded from " + getUrl());
                }
            } catch (Exception e) {
                fiData = Collections.emptyMap();
            }
        }

        return fiData;
    }

    private void initializeFIData() throws IOException, SAXException {
        URL url = new URL(getUrl());
        XMLReader xmlReader = new Parser();
        xmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
        xmlReader.setFeature("http://xml.org/sax/features/validation", false);
        xmlReader.setContentHandler(new DirectoryContentHandler());
        xmlReader.parse(new InputSource(url.openStream()));
    }

    private BaseFinancialInstitutionData loadInstitutionData(String href) throws IOException, SAXException {
        if (LOG.isInfoEnabled()) {
            LOG.info("Loading institution data from: " + href);
        }

        URL url = new URL(href);
        XMLReader xmlReader = new Parser();
        xmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
        xmlReader.setFeature("http://xml.org/sax/features/validation", false);
        InstitutionContentHandler institutionHandler = new InstitutionContentHandler();
        xmlReader.setContentHandler(institutionHandler);
        xmlReader.parse(new InputSource(url.openStream()));
        return institutionHandler.data;
    }

    /**
     * The URL of the OFX home directory.
     *
     * @return The URL of the OFX home directory.
     */
    public String getUrl() {
        return url;
    }

    /**
     * The URL of the OFX home directory.
     *
     * @param url The URL of the OFX home directory.
     */
    public void setUrl(String url) {
        this.url = url;
    }

    private class DirectoryContentHandler extends DefaultHandler {

        private Set<String> visited = new TreeSet<String>();

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
                throws SAXException {
            if ("a".equalsIgnoreCase(qName) || "a".equalsIgnoreCase(localName)) {
                for (int i = 0; i < attributes.getLength(); i++) {
                    String href = attributes.getValue("href");
                    if (href != null) {
                        Matcher matcher = INSTITUTION_HREF_PATTERN.matcher(href);
                        if (matcher.matches() && visited.add(href)) {
                            try {
                                BaseFinancialInstitutionData data = loadInstitutionData(href);
                                data.setName(attributes.getValue("title"));
                                fiData.put(data.getId(), data);
                            } catch (IOException e) {
                                throw new SAXException(e);
                            }
                        }
                    }
                }
            }
        }
    }

    private enum FIField {
        ID,

        ORG,

        URL,

        BROKERID
    }

    private class InstitutionContentHandler extends DefaultHandler {

        private FIField currentField = null;
        private StringBuilder fieldNameBuffer = null;
        private StringBuilder fieldValueBuffer = null;
        private BaseFinancialInstitutionData data;

        @Override
        public void startDocument() throws SAXException {
            this.data = new BaseFinancialInstitutionData(UUID.randomUUID().toString());
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
                throws SAXException {
            if ("td".equalsIgnoreCase(qName)) {
                if (this.fieldNameBuffer == null) {
                    this.fieldNameBuffer = new StringBuilder();
                } else if (currentField != null) {
                    this.fieldValueBuffer = new StringBuilder();
                }
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if ("td".equalsIgnoreCase(qName)) {
                if (currentField == null) {
                    if (fieldNameBuffer != null) {
                        String fieldName = this.fieldNameBuffer.toString().toLowerCase();
                        if (fieldName.contains("fi id")) {
                            this.currentField = FIField.ID;
                            return;
                        } else if (fieldName.contains("fi org")) {
                            this.currentField = FIField.ORG;
                            return;
                        } else if (fieldName.contains("fi url")) {
                            this.currentField = FIField.URL;
                            return;
                        } else if (fieldName.contains("fi broker id")) {
                            this.currentField = FIField.BROKERID;
                            return;
                        }
                    }
                } else if (this.fieldValueBuffer != null) {
                    String fieldValue = this.fieldValueBuffer.toString().trim();
                    if ("".equals(fieldValue)) {
                        fieldValue = null;
                    }

                    switch (currentField) {
                    case ID:
                        this.data.setFinancialInstitutionId(fieldValue);
                        break;
                    case BROKERID:
                        this.data.setBrokerId(fieldValue);
                        break;
                    case ORG:
                        this.data.setOrganization(fieldValue);
                        break;
                    case URL:
                        try {
                            this.data.setOFXURL(new URL(fieldValue));
                        } catch (MalformedURLException e) {
                            //fall through...
                        }
                    }
                }

                this.currentField = null;
                this.fieldNameBuffer = null;
                this.fieldValueBuffer = null;
            }
        }

        @Override
        public void characters(char ch[], int start, int length) throws SAXException {
            if (this.currentField != null && fieldValueBuffer != null) {
                fieldValueBuffer.append(ch, start, length);
            } else if (fieldNameBuffer != null) {
                fieldNameBuffer.append(ch, start, length);
            }
        }
    }

}