com.jasonstedman.maven.RequireConfigTransformer.java Source code

Java tutorial

Introduction

Here is the source code for com.jasonstedman.maven.RequireConfigTransformer.java

Source

package com.jasonstedman.maven;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.apache.maven.plugins.shade.relocation.Relocator;
import org.apache.maven.plugins.shade.resource.ResourceTransformer;

import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;

/* Copyright 2014 Jason Stedman
 *
 * 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.
 */

/**
 * This ResourceTransformer implementation merges RequireJS configurations
 * from multiple war projects into a unified data-main script. This enables
 * modular JavaScript development using Maven for dependency management
 * without imposing architectural choices on the user.
 * 
 */

public class RequireConfigTransformer implements ResourceTransformer {

    public String dataMainPath;
    public String configFilePattern;
    public String initialDefinition;

    private Pattern configFilePatternInstance;
    private ArrayList<Map<String, Object>> configBlocks = new ArrayList<Map<String, Object>>();
    private String initBlock;
    private boolean hasTransformedResource = false;

    private static ObjectMapper mapper = new ObjectMapper();
    private static Logger logger;

    static {
        mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        logger = Logger.getLogger("RequireConfigTransformer");
    }

    public boolean canTransformResource(String resource) {
        configFilePatternInstance = Pattern.compile(configFilePattern);
        if (resource.equals(initialDefinition)) {
            logger.fine(resource + " matches " + initialDefinition);
            return true;
        }
        if (configFilePatternInstance.matcher(resource).matches()) {
            logger.fine(resource + " matches " + configFilePattern);
            return true;
        } else
            return false;
    }

    @SuppressWarnings("unchecked")
    public void processResource(String resource, InputStream is, List<Relocator> relocators) throws IOException {
        String block = IOUtils.toString(is, "utf-8");
        if (resource.equals(initialDefinition)) {
            initBlock = block;
        } else if (configFilePatternInstance.matcher(resource).matches()) {
            block = block.replaceFirst("\\s*require\\s*.\\s*config\\s*\\(", "");
            block = block.substring(0, block.lastIndexOf(')'));
            logger.finer(block);
            configBlocks.add(mapper.readValue(block, Map.class));
        }
        hasTransformedResource = true;
    }

    public boolean hasTransformedResource() {
        return hasTransformedResource;
    }

    public void modifyOutputStream(JarOutputStream jos) throws IOException {
        logger.info("Creating merged data-main script at war path : " + dataMainPath);
        logger.info("Merging require configs matching path pattern : " + configFilePattern);
        logger.info("Using initial define block from : " + initialDefinition);

        Map<String, Object> configBlock = mergeConfigBlocks();

        jos.putNextEntry(new JarEntry(dataMainPath));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(jos));
        writer.write("require.config(");
        writer.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(configBlock));
        writer.write(");");
        writer.newLine();
        writer.write(initBlock);
        writer.flush();
    }

    private Map<String, Object> mergeConfigBlocks() {
        HashMap<String, Object> configBlock = new HashMap<String, Object>();
        for (Map<String, Object> entry : configBlocks) {
            mergeMaps(configBlock, entry);
        }
        return configBlock;
    }

    @SuppressWarnings("unchecked")
    private void mergeMaps(Map<String, Object> a, Map<String, Object> b) {
        for (String key : b.keySet()) {
            Object value = b.get(key);
            if (value instanceof String) {
                if (a.containsKey(key) && !a.get(key).equals(value)) {
                    throw new RuntimeException("duplicate config for key : " + key);
                } else {
                    a.put(key, value);
                }
            } else if (value instanceof Map) {
                if (a.containsKey(key)) {
                    mergeMaps((Map<String, Object>) a.get(key), (Map<String, Object>) value);
                } else {
                    a.put(key, value);
                }
            }
        }
    }

}