Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ /* $Id: PSDictionary.java 1345683 2012-06-03 14:50:33Z gadams $ */ package org.apache.xmlgraphics.ps; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; // CSOFF: InnerAssignment /** * This class is used to encapsulate postscript dictionary objects. */ public class PSDictionary extends HashMap<String, Object> { private static final long serialVersionUID = 815367222496219197L; /** * This class is used to parse dictionary strings. */ private static class Maker { /** * Simple token holding class */ private class Token { /** * start index in string */ private int startIndex = -1; /** * end index in string */ private int endIndex = -1; /** * token string value */ private String value; } private static final String[][] BRACES = { { "<<", ">>" }, { "[", "]" }, { "{", "}" }, { "(", ")" } }; private static final int OPENING = 0; private static final int CLOSING = 1; private static final int DICTIONARY = 0; private static final int ARRAY = 1; private static final int PROCEDURE = 2; private static final int STRING = 3; /** * Returns a Token containing the start, end index and value of the next * token found in a given string * * @param str * string to search * @param fromIndex * search from index * @return Token containing the start, end index and value of the next * token */ protected Token nextToken(final String str, final int fromIndex) { Token t = null; for (int i = fromIndex; i < str.length(); ++i) { final boolean isWhitespace = Character.isWhitespace(str.charAt(i)); // start index found if (t == null && !isWhitespace) { t = new Token(); t.startIndex = i; // end index found } else if (t != null && isWhitespace) { t.endIndex = i; break; } } // start index found if (t != null) { // end index not found so take end of string if (t.endIndex == -1) { t.endIndex = str.length(); } t.value = str.substring(t.startIndex, t.endIndex); } return t; } /** * Returns the closing brace index from a given string searches from a * given index * * @param str * string to search * @param braces * string array of opening and closing brace * @param fromIndex * searches from index * @return matching brace index * @throws org.apache.xmlgraphics.ps.PSDictionaryFormatException * thrown in the event that a parsing error occurred */ private int indexOfMatchingBrace(final String str, final String[] braces, final int inIndex) throws PSDictionaryFormatException { int fromIndex = inIndex; final int len = str.length(); if (braces.length != 2) { throw new PSDictionaryFormatException("Wrong number of braces"); } for (int openCnt = 0, closeCnt = 0; fromIndex < len; ++fromIndex) { if (str.startsWith(braces[OPENING], fromIndex)) { ++openCnt; } else if (str.startsWith(braces[CLOSING], fromIndex)) { ++closeCnt; if (openCnt > 0 && openCnt == closeCnt) { return fromIndex; // found } } } return -1; // not found } /** * Strips braces from complex object string * * @param str * String to parse * @param braces * String array containing opening and closing braces * @return String with braces stripped * @throws org.apache.xmlgraphics.ps.PSDictionaryFormatException * thrown in the event that a parsing error occurred */ private String stripBraces(final String str, final String[] braces) throws PSDictionaryFormatException { // find first opening brace final int firstIndex = str.indexOf(braces[OPENING]); if (firstIndex == -1) { throw new PSDictionaryFormatException("Failed to find opening parameter '" + braces[OPENING] + ""); } // find last matching brace final int lastIndex = indexOfMatchingBrace(str, braces, firstIndex); if (lastIndex == -1) { throw new PSDictionaryFormatException( "Failed to find matching closing parameter '" + braces[CLOSING] + "'"); } // strip brace and trim final int braceLen = braces[OPENING].length(); return str.substring(firstIndex + braceLen, lastIndex).trim(); } /** * Parses a dictionary string and provides a dictionary object * * @param str * a dictionary string * @return A postscript dictionary object * @throws org.apache.xmlgraphics.ps.PSDictionaryFormatException * thrown in the event that a parsing error occurred */ public PSDictionary parseDictionary(final String inStr) throws PSDictionaryFormatException { final PSDictionary dictionary = new PSDictionary(); final String str = stripBraces(inStr.trim(), BRACES[DICTIONARY]); // length of dictionary string final int len = str.length(); Token keyToken; for (int currIndex = 0; (keyToken = nextToken(str, currIndex)) != null && currIndex <= len;) { if (keyToken.value == null) { throw new PSDictionaryFormatException("Failed to parse object key"); } final Token valueToken = nextToken(str, keyToken.endIndex + 1); String[] braces = null; for (final String[] element : BRACES) { if (valueToken.value.startsWith(element[OPENING])) { braces = element; break; } } Object obj = null; if (braces != null) { // find closing brace valueToken.endIndex = indexOfMatchingBrace(str, braces, valueToken.startIndex) + braces[OPENING].length(); if (valueToken.endIndex < 0) { throw new PSDictionaryFormatException("Closing value brace '" + braces[CLOSING] + "' not found for key '" + keyToken.value + "'"); } valueToken.value = str.substring(valueToken.startIndex, valueToken.endIndex); } if (braces == null || braces == BRACES[PROCEDURE] || braces == BRACES[STRING]) { obj = valueToken.value; } else if (BRACES[ARRAY] == braces) { final List<String> objList = new ArrayList<>(); final String objString = stripBraces(valueToken.value, braces); final StringTokenizer tokenizer = new StringTokenizer(objString, ","); while (tokenizer.hasMoreTokens()) { objList.add(tokenizer.nextToken()); } obj = objList; } else if (BRACES[DICTIONARY] == braces) { obj = parseDictionary(valueToken.value); } dictionary.put(keyToken.value, obj); currIndex = valueToken.endIndex + 1; } return dictionary; } } /** * Parses a given a dictionary string and returns an object * * @param str * dictionary string * @return dictionary object * @throws org.apache.xmlgraphics.ps.PSDictionaryFormatException * thrown in the event that a parsing error occurred */ public static PSDictionary valueOf(final String str) throws PSDictionaryFormatException { return new Maker().parseDictionary(str); } /** * @param obj * object to test equality against * @return whether a given object is equal to this dictionary object * @see java.lang.Object#equals(Object) */ @Override public boolean equals(final Object obj) { if (!(obj instanceof PSDictionary)) { return false; } final PSDictionary dictionaryObj = (PSDictionary) obj; if (dictionaryObj.size() != size()) { return false; } for (final String key : keySet()) { if (!dictionaryObj.containsKey(key)) { return false; } if (!dictionaryObj.get(key).equals(get(key))) { return false; } } return true; } /** {@inheritDoc} */ @Override public int hashCode() { int hashCode = 7; for (final Object value : values()) { hashCode += value.hashCode(); } return hashCode; } /** {@inheritDoc} */ @Override public String toString() { if (isEmpty()) { return ""; } final StringBuilder sb = new StringBuilder("<<\n"); for (final String key : super.keySet()) { sb.append(" " + key + " "); final Object obj = super.get(key); if (obj instanceof ArrayList) { @SuppressWarnings("unchecked") final List<Object> array = (List<Object>) obj; String str = "["; str += StringUtils.join(array, " "); str = str.trim(); str += "]"; sb.append(str + "\n"); } else { sb.append(obj.toString() + "\n"); } } sb.append(">>"); return sb.toString(); } }