com.day.cq.wcm.foundation.ImageMap.java Source code

Java tutorial

Introduction

Here is the source code for com.day.cq.wcm.foundation.ImageMap.java

Source

/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.cq.wcm.foundation;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is used for parsing image map definitions as generated by the
 * <code>CqImageMap</code> component and make them available through an API to
 * CQ components.
 * <p>
 * The class is designed for single-threaded use.
 */
public class ImageMap {

    /**
     * Inner class that represents an area of the map.
     */
    public class ImageArea {

        /**
         * type of the area (as required by the <i>area</i> tag)
         */
        private final String type;

        /**
         * coordinates of the area (as required by the <i>area</i> tag)
         */
        private final String coordinates;

        /**
         * HREF of the area (<code>null</code> if none is specified)
         */
        private final String href;

        /**
         * alternative text of the area (unicoded; <code>null</code> if none is specified)
         */
        private final String altText;

        /**
         * target of the area's HREF (<code>null</code> if none is specified)
         */
        private final String target;

        /**
         * Creates a new <code>ImageArea</code> with the given parameters.
         *
         * @param type type/shape of the image area
         * @param coordinates coordinates
         * @param href HREF (optional)
         * @param altText alternative text (optional)
         * @param target target (frame/window; optional)
         */
        protected ImageArea(String type, String coordinates, String href, String altText, String target) {
            log.debug("Creating image area; destination: {}; altText: {}; target: {}",
                    new Object[] { href, altText, target });
            this.type = type;
            this.coordinates = coordinates;
            this.href = href;
            this.altText = altText;
            this.target = target;
        }

        /**
         * Gets the type (= <i>shape</i> attribute) of the image area.
         *
         * @return type of the image area
         */
        public String getType() {
            return type;
        }

        /**
         * Gets the coordinates (= <i>coords</i> attribute) of the image area.
         *
         * @return coordinates of the image area
         */
        public String getCoordinates() {
            return coordinates;
        }

        /**
         * Gets the (optional) HREF of the image area.
         *
         * @return HREF of the image area; <code>null</code> if undefined
         */
        public String getHref() {
            return href;
        }

        /**
         * Gets the alternative text of the image area in unicode format.
         *
         * @return alternative text of the image area; <code>null</code> if undefined
         */
        public String getAltText() {
            return altText;
        }

        /**
         * Gets the target frame/window of the image area.
         *
         * @return target frame/window of the image area; <code>null</code> if undefined
         */
        public String getTarget() {
            return target;
        }
    }

    /**
     * internal logger
     */
    private static final Logger log = LoggerFactory.getLogger(ImageMap.class);

    /**
     * areas of the map
     */
    private final List<ImageArea> areas;

    /**
     * Creates a new <code>ImageMap</code> object from the given string representation.
     *
     * @param mapDefinition string representation (as created by the
     *                      <code>CqImageMap</code> component)
     * @return a suitable representation of the component
     * @throws IllegalArgumentException if the given string representation is invalid
     */
    public static ImageMap fromString(String mapDefinition) throws IllegalArgumentException {
        ImageMap theMap = new ImageMap();
        theMap.createFromString(mapDefinition);
        return theMap;
    }

    /**
     * Private constructor.
     */
    private ImageMap() {
        this.areas = new ArrayList<ImageArea>(10);
    }

    /**
     * Decodes a string (encoded with <code>encodeString()</code> that is contained in another
     * string. The (partial) string to parse has to be enclosed in quotation marks.
     * <p>
     * For example:<br>
     * <code>decodeFromContainingString("x:\&quot;abc\\&quot;\&quot;", 2)</code> will return
     * <code>{ "decoded": "abc&quot;", "nextPos": 9 }</code>
     *
     * @param containingString the containing string
     * @param parseStartPos position where parsing should start
     * @return decoding result; <code>Object[]</code> with the decoded string as first
     *         element (type <code>String</code>); the first character position after the
     *         closing quotation as second element (type <code>Integer</code>)
     * @throws IllegalArgumentException if the string has an invalid format
     */
    private Object[] decodeFromContainingString(String containingString, int parseStartPos) {
        int quotPos = containingString.indexOf("\"", parseStartPos);
        if (quotPos < 0) {
            throw new IllegalArgumentException("No opening quotation mark found in string.");
        }
        boolean isDone = false;
        int currentCharPos = quotPos + 1;
        StringBuilder text = new StringBuilder(128);
        boolean isEscaped = false;
        while (!isDone) {
            char charToProcess = containingString.charAt(currentCharPos);
            if ((charToProcess == '\"') && (!isEscaped)) {
                isDone = true;
            } else if (charToProcess == '\\') {
                if (isEscaped) {
                    text.append("\\");
                    isEscaped = false;
                } else {
                    isEscaped = true;
                }
            } else if (isEscaped) {
                text.append(charToProcess);
                isEscaped = false;
            } else {
                text.append(charToProcess);
            }
            currentCharPos++;
            if ((currentCharPos >= containingString.length()) && (!isDone)) {
                throw new IllegalArgumentException("No final quotation mark found in string.");
            }
        }
        return new Object[] { text.toString(), currentCharPos };
    }

    /**
     * Parses the destination from the given image map string (full version).
     *
     * @param value image map definition (full version)
     * @param parseStartPos start position (in <code>version</code>) where the destination
     *                      has to be parsed from
     * @return array with url as first element, target as second, text as third (all of them
     *         of type <code>String</code> and the position of the next character to parse
     *         as fourth element (<code>Integer</code>
     * @throws IllegalArgumentException if the string definition has a non-parseable format
     */
    private Object[] parseAreaDestination(String value, int parseStartPos) throws IllegalArgumentException {
        int parsePos = parseStartPos;
        Object[] parseResult;
        char charToCheck;
        String url = null;
        if (parsePos < value.length()) {
            charToCheck = value.charAt(parsePos);
            if (charToCheck != '|') {
                parseResult = this.decodeFromContainingString(value, parsePos);
                url = (String) parseResult[0];
                parsePos = (Integer) parseResult[1];
            }
            parsePos++;
        }
        String target = null;
        if (parsePos < value.length()) {
            charToCheck = value.charAt(parsePos);
            if (charToCheck != '|') {
                parseResult = this.decodeFromContainingString(value, parsePos);
                target = (String) parseResult[0];
                parsePos = (Integer) parseResult[1];
            }
            parsePos++;
        }
        String text = null;
        if (parsePos < value.length()) {
            charToCheck = value.charAt(parsePos);
            if (charToCheck != ']') {
                parseResult = this.decodeFromContainingString(value, parsePos);
                if (parseResult == null) {
                    return null;
                } else {
                    text = (String) parseResult[0];
                    parsePos = (Integer) parseResult[1];
                }
            }
        }
        return new Object[] { url, target, text, parsePos + 1 };
    }

    /**
     * Creates a new <code>ImageMap</code> object from the given string representation.
     *
     * @param strDefinition string representation (as created by the
     *                      <code>CqImageMap</code> component)
     * @throws IllegalArgumentException if the given string representation is invalid
     */
    private void createFromString(String strDefinition) throws IllegalArgumentException {
        this.areas.clear();
        int processingPos = 0;
        while (processingPos < strDefinition.length()) {
            int startPos = strDefinition.indexOf("[", processingPos);
            if (startPos < 0) {
                break;
            }
            int coordEndPos = strDefinition.indexOf(")", startPos + 1);
            if (coordEndPos < 0) {
                break;
            }
            String areaDef = strDefinition.substring(startPos + 1, coordEndPos + 1);
            int contentStartPos = areaDef.indexOf("(");
            if (contentStartPos < 0) {
                throw new IllegalArgumentException(
                        "Could not create image map; area " + "definition is not in format 'type(coords)...'.");
            }
            String type = areaDef.substring(0, contentStartPos);
            String coords = areaDef.substring(contentStartPos + 1, areaDef.length() - 1);
            Object[] areaDestination = this.parseAreaDestination(strDefinition, coordEndPos + 1);
            processingPos = (Integer) areaDestination[3];
            String url = (String) areaDestination[0];
            String target = (String) areaDestination[1];
            String text = (String) areaDestination[2];
            this.areas.add(new ImageArea(type, coords, url, text, target));
        }
    }

    /**
     * Returns the HTML code required for the image map.
     * <p>
     * Parameters <code>originalSize</code> and <code>scaled</code> can be used to create
     * a scaled map that is suitable for a scaled image.
     *
     * @param id id of the image map
     * @return HTML code representing the image map
     */
    public String draw(String id) {
        StringBuilder htmlCode = new StringBuilder(128);
        htmlCode.append("<map name=\"").append(id).append("\">");
        for (ImageArea areaToDraw : this.areas) {
            htmlCode.append("<area shape=\"").append(areaToDraw.getType()).append("\"");
            String coords = areaToDraw.getCoordinates();
            htmlCode.append(" coords=\"").append(coords).append("\"");
            String href = areaToDraw.getHref();
            if (href != null) {
                if (href.startsWith("/")) {
                    int extensionPos = href.lastIndexOf(".");
                    int slashPos = href.lastIndexOf("/");
                    if ((extensionPos < 0) || (extensionPos < slashPos)) {
                        href += ".html";
                    }
                }
                htmlCode.append(" href=\"").append(href).append("\"");
            }
            String altText = areaToDraw.getAltText();
            if (altText != null) {
                htmlCode.append(" alt=\"").append(StringEscapeUtils.escapeHtml4(altText)).append("\"");
                htmlCode.append(" title=\"").append(StringEscapeUtils.escapeHtml4(altText)).append("\"");
            }
            String target = areaToDraw.getTarget();
            if (target != null) {
                htmlCode.append(" target=\"").append(target).append("\"");
            }
            htmlCode.append(">");
        }
        htmlCode.append("</map>");
        log.debug("Image map HTML code: {}", htmlCode);
        return htmlCode.toString();
    }

}