Java tutorial
/* * 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:\"abc\\"\"", 2)</code> will return * <code>{ "decoded": "abc"", "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(); } }