org.jzkit.search.util.RecordConversion.FragmentTransformerService.java Source code

Java tutorial

Introduction

Here is the source code for org.jzkit.search.util.RecordConversion.FragmentTransformerService.java

Source

// Title:       FragmentTransformerService
// @version:    $Id: FragmentTransformerService.java,v 1.6 2004/11/28 15:59:01 ibbo Exp $
// Copyright:   Copyright (C) 1999,2000 Knowledge Integration Ltd.
// @author:     Ian Ibbotson (ibbo@k-int.com)
// Company:     Knowledge Integration Ltd.
// Description: 

//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2.1 of
// the license, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite
// 330, Boston, MA  02111-1307, USA.
// 

package org.jzkit.search.util.RecordConversion;

import java.util.*;
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;

import org.w3c.dom.*;

import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.*;
import javax.xml.transform.stream.StreamResult;
import javax.naming.Reference;
import javax.naming.NamingException;

import java.util.prefs.Preferences;

import java.lang.reflect.Constructor;

import org.jzkit.search.util.RecordModel.*;
import org.jzkit.configuration.api.*;
import org.springframework.context.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Convert a fragment with a given specification to a target spec.
 */
public class FragmentTransformerService implements ApplicationContextAware {

    private String template_source;
    private static String file_sep = System.getProperty("file.separator");
    private Map context = new HashMap();
    private Map type_register = new HashMap();
    private static Log log = LogFactory.getLog(FragmentTransformerService.class);
    private List all_transformations = new ArrayList();
    private ApplicationContext ctx;

    /**
     * A Hashtable of Hashtables that points from a source schema
     * to schemas that we can get to by applying known transformations
     */
    private Map known_schemas = new HashMap();

    /**
     * Hashtable of stylesheet file names to Templates objects
     */
    private Map known_transformers = new HashMap();
    private TransformerFactory tFactory = TransformerFactory.newInstance();
    private DocumentBuilder docBuilder;
    private Configuration configuration = null;

    public FragmentTransformerService(Configuration configuration) {
        log.debug("new FragmentTransformerService");
        this.configuration = configuration;
    }

    public void init() {
        log.debug("FragmentTransformerService::init");
        try {
            for (Iterator i = configuration.getRegisteredConverterTypes(); i.hasNext();) {
                Object o = i.next();
                RecordTransformerTypeInformationDBO rtti = (RecordTransformerTypeInformationDBO) o;
                registerType(rtti.getType(), rtti.getClassname());
            }

            for (Iterator i = configuration.getRegisteredRecordMappings(); i.hasNext();) {
                RecordMappingInformationDBO rmi = (RecordMappingInformationDBO) i.next();
                registerMapping(rmi.getFromSpec(), rmi.getToSpec(), rmi.getType(), rmi.getResource());
            }

            DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
            dfactory.setNamespaceAware(true);
            docBuilder = dfactory.newDocumentBuilder();
            context.put("DocBuilder", docBuilder);
            context.put("TransformerFactory", tFactory);
        } catch (org.jzkit.configuration.api.ConfigurationException ce) {
            throw new RuntimeException(ce.toString());
        } catch (ParserConfigurationException pce) {
            throw new RuntimeException(pce.toString());
        } finally {
            // configuration.close();
        }
    }

    public void setTemplateSource(String directory) {
        // cat.debug("setTemplateSource("+directory+")");
        this.template_source = directory;
    }

    public void registerType(String type, String classname) {
        log.debug("Register Type " + type + " class=" + classname);

        try {
            Class c = Class.forName(classname);
            type_register.put(type, c);
        } catch (Exception e) {
            log.warn("Problem: ", e);
            throw new RuntimeException("Problem with transformer class", e);
        }
    }

    public void registerMapping(String from, String to, String type, String sheet) {
        log.debug("from " + from + " to " + to + " sheet=" + sheet + " type=" + type);

        try {
            Map props = new HashMap();
            props.put("Sheet", sheet);

            Class required_type = (Class) type_register.get(type);

            if (required_type != null) {

                Class[] param_types = new Class[] { String.class, String.class, Map.class, Map.class,
                        ApplicationContext.class };
                Constructor c = required_type.getConstructor(param_types);
                if (c != null) {
                    Object params[] = new Object[] { from, to, props, context, ctx };
                    FragmentTransformer t = (FragmentTransformer) c.newInstance(params);

                    all_transformations.add(t);
                } else {
                    log.error("No constructor available for class " + required_type.getName());
                }
            } else {
                log.error("Unable to resolve a class for mapping " + type);
            }
        } catch (Exception e) {
            log.error("Problem registering mapping " + sheet + ". Mapping will not be available", e);
        }
    }

    public Object convert(Document source, String from_schema, String to_schema, Map trans_properties)
            throws FragmentTransformationException {

        if (source == null)
            throw new FragmentTransformationException("No source record for transformation");

        Object result = source;
        Document next_input = source;

        log.debug("Attempting to find transform from " + from_schema + " to " + to_schema);
        List templates = findTransformationSequence(from_schema, to_schema);

        // This supplies us with a vector of FragmentTransformation objects which propose different
        // kinds of transformation which should be applied of the source document in order to
        // generate the target schema

        if (templates == null) {
            throw new FragmentTransformationException(
                    "No transformation templates available (trying " + from_schema + " to " + to_schema + ")");
        }

        log.debug("Located path : " + templates);

        // For each template
        for (Iterator templates_enum = templates.iterator(); templates_enum.hasNext();) {
            // String template_name = (String)templates.elementAt(i);
            FragmentTransformer transformation = (FragmentTransformer) templates_enum.next();

            // cat.debug("Transform...");
            result = transformation.transform(next_input, trans_properties);

            // Is there another step in the transformation?
            if ((templates_enum.hasNext()) && (result instanceof Document)) {
                // Yes, so use the document resulting from the last transformation as the source document for the next one
                // cat.debug("Passing result of intermediate transformation into next transformer");
                next_input = (Document) result;
            }
        }

        return result;
    }

    public List lookupTransformersMatchingSource(List path_so_far, String source) {
        // Source should be a real stringified RecordFormatSpecification, possibly with nulls for
        // syntax and element set name. Each transformer object has a regexp match expression that
        // represents the incoming specs it is willing to accept. Each time this function is called
        // It must iterate over the set of available transforms, looking for ones whos regexp
        // matches source.
        List retval = new ArrayList();
        log.debug("lookupTransformersMatchingSource sz=" + all_transformations.size() + " src=" + source);

        for (Iterator e = all_transformations.iterator(); e.hasNext();) {
            FragmentTransformer ft = (FragmentTransformer) e.next();

            if ((path_so_far != null) && (path_so_far.contains(ft))) {
                log.warn("CYCLE DETECTED in transformation graph.. not re-adding target " + ft.getToSchema());
            } else if (ft.isApplicableTo(source)) {
                log.debug("Adding transform to " + ft.getToSchema());
                retval.add(ft);
            }
        }

        log.debug("lookupTransformersMatchingSource result=" + retval.size());
        return retval;
    }

    public List findTransformationSequence(String source, String target) {
        // log.fine("findTransformationSequence("+source+","+target+")");

        List possible_transform_sequences = new ArrayList();
        List possible_transforms_from_source = lookupTransformersMatchingSource(null, source);

        for (Iterator e = possible_transforms_from_source.iterator(); e.hasNext();) {
            List new_path = new ArrayList();
            new_path.add(e.next());
            possible_transform_sequences.add(new_path);
        }
        return breadthFirst(target, possible_transform_sequences);
    }

    /**
     * Perform a breadth first search of the available mappings, ending when we have
     * exhausted the search tree or the result of a transformation matches target.
     * @param target The sought target spec.
     * @param possible_transformation_set A List of List representing all current possible paths.
     */
    public List breadthFirst(String target, List possible_transformation_set) {
        List result = null;

        // Any possible transforrmations?
        if ((possible_transformation_set == null) || (possible_transformation_set.size() == 0)) {
            // nope.. can't be done :(
            return null;
        }

        // We have potentials... See if any of the current set of possible transformations ends with target
        for (Iterator e = possible_transformation_set.iterator(); e.hasNext();) {
            // Examine each path
            List current_path = (List) e.next();

            // Get the result spec
            FragmentTransformer ft = (FragmentTransformer) current_path.get(current_path.size() - 1);

            // Does it "Match" the target. Target should really be a regexp, with some scoring for exact match over wildcards.
            if (ft.getToSchema().equalsIgnoreCase(target))
                result = current_path;
        }

        // Did we match?
        if (result != null) {
            // Yep  :)
            return result;
        } else {
            // No.. So expand possible_transformation_step with the next round of nodes.. Yep, it's expensive :(
            List new_transformation_set = new ArrayList();

            // for each potential path
            for (Iterator e2 = possible_transformation_set.iterator(); e2.hasNext();) {
                // Get hold of the vector of transformations represented by that path
                List current_path_to_expand = (List) e2.next();

                // Look up the last transformation on the path
                FragmentTransformer ft = (FragmentTransformer) current_path_to_expand
                        .get(current_path_to_expand.size() - 1);

                // Generate a list of transformations applicable given the output of that transformation
                List possible_next_steps = lookupTransformersMatchingSource(current_path_to_expand,
                        ft.getToSchema());

                // Add each of those potential transformations to the new set
                for (Iterator e3 = possible_next_steps.iterator(); e3.hasNext();) {
                    // Clone the current path
                    List new_path = new ArrayList();
                    new_path.addAll(current_path_to_expand);
                    // And add on this new possible transformation
                    new_path.add(e3.next());
                    // Finally, add this new path to the new_transformation_set
                    new_transformation_set.add(new_path);
                }
            }

            return breadthFirst(target, new_transformation_set);
        }

    }

    public Reference getReference() throws NamingException {
        return new Reference(FragmentTransformerService.class.getName());
    }

    public DOMResult createEmptyDOMResult() {
        return new DOMResult(docBuilder.newDocument());
    }

    public void setApplicationContext(ApplicationContext ctx) {
        this.ctx = ctx;
    }

}