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. */ package org.apache.commons.scxml2.io; import java.io.IOException; import java.io.InputStream; import java.net.URL; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.IOUtils; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.SAXException; /** * The ContentParser provides utility methods for cleaning content strings and parsing them into "raw" content model Objects */ public class ContentParser { public static final ContentParser DEFAULT_PARSER = new ContentParser(); /** * Jackson JSON ObjectMapper */ private ObjectMapper jsonObjectMapper; /** * Default constructor initializing a Jackson ObjectMapper allowing embedded comments, including YAML style */ public ContentParser() { this.jsonObjectMapper = new ObjectMapper(); jsonObjectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); jsonObjectMapper.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true); } /** * Constructor with a custom configured Jackson ObjectMapper * @param jsonObjectMapper custom configured Jackson ObjectMapper */ public ContentParser(final ObjectMapper jsonObjectMapper) { this.jsonObjectMapper = jsonObjectMapper; } /** * Trim pre/post-fixed whitespace from content string * @param content content to trim * @return trimmed content */ public static String trimContent(final String content) { if (content != null) { int start = 0; int length = content.length(); while (start < length && isWhiteSpace(content.charAt(start))) { start++; } while (length > start && isWhiteSpace(content.charAt(length - 1))) { length--; } if (start == length) { return ""; } return content.substring(start, length); } return null; } /** * Space normalize content string, trimming pre/post-fixed whitespace and collapsing embedded whitespaces to * single space. * @param content content to space-normalize * @return space-normalized content */ public static String spaceNormalizeContent(final String content) { if (content != null) { int index = 0; int length = content.length(); StringBuilder buffer = new StringBuilder(length); boolean whiteSpace = false; while (index < length) { if (isWhiteSpace(content.charAt(index))) { if (!whiteSpace && buffer.length() > 0) { buffer.append(' '); whiteSpace = true; } } else { buffer.append(content.charAt(index)); whiteSpace = false; } index++; } if (whiteSpace) { buffer.setLength(buffer.length() - 1); } return buffer.toString(); } return null; } /** * Check if a character is whitespace (space, tab, newline, cr) or not * @param c character to check * @return true if character is whitespace */ public static boolean isWhiteSpace(final char c) { return c == ' ' || c == '\n' || c == '\t' || c == '\r'; } /** * Check if content starts with JSON object '{' or array '[' marker * @param content text to check * @return true if content start with '{' or '[' character */ public static boolean hasJsonSignature(final String content) { final char c = content.length() > 0 ? content.charAt(0) : 0; return c == '{' || c == '['; } /** * Check if content indicates its an XML document * @param content content to check * @return true if content indicates its an XML document */ public static boolean hasXmlSignature(final String content) { return content != null && content.startsWith("<?xml "); } /** * Parse and map JSON string to 'raw' Java Objects: object -> LinkedHashMap, array -> ArrayList * @param jsonString JSON string to parse * @return 'raw' mapped Java Object for JSON string * @throws IOException In case of parsing exceptions */ public Object parseJson(final String jsonString) throws IOException { return jsonObjectMapper.readValue(jsonString, Object.class); } /** * Parse an XML String and return the document element * @param xmlString XML String to parse * @return document element * @throws IOException */ public Node parseXml(final String xmlString) throws IOException { Document doc = null; try { doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlString); } catch (SAXException e) { throw new IOException(e); } catch (ParserConfigurationException e) { throw new IOException(e); } return doc != null ? doc.getDocumentElement() : null; } /** * Parse a string into a content object, following the SCXML rules as specified for the ECMAscript (section B.2.1) Data Model * <ul> * <li>if the content can be interpreted as JSON, it will be parsed as JSON into an 'raw' object model</li> * <li>if the content can be interpreted as XML, it will be parsed into a XML DOM element</li> * <li>otherwise the content will be treated (cleaned) as a space-normalized string literal</li> * </ul> * @param content the content to parse * @return the parsed content object * @throws IOException In case of parsing exceptions */ public Object parseContent(final String content) throws IOException { if (content != null) { String src = trimContent(content); if (hasJsonSignature(src)) { return parseJson(src); } else if (hasXmlSignature(src)) { return parseXml(src); } return spaceNormalizeContent(src); } return null; } /** * Load a resource (URL) as an UTF-8 encoded content string to be parsed into a content object through {@link #parseContent(String)} * @param resourceURL Resource URL to load content from * @return the parsed content object * @throws IOException In case of loading or parsing exceptions */ public Object parseResource(final String resourceURL) throws IOException { InputStream in = null; try { in = new URL(resourceURL).openStream(); String content = IOUtils.toString(in, "UTF-8"); return parseContent(content); } finally { IOUtils.closeQuietly(in); } } }