001/* 002 * (C) Copyright 2006-2014 Nuxeo SA <http://nuxeo.com> and others 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Jean-Marc Orliaguet, Chalmers 011 * 012 */ 013 014package org.nuxeo.theme; 015 016import java.io.ByteArrayOutputStream; 017import java.io.File; 018import java.io.IOException; 019import java.io.InputStream; 020import java.io.OutputStream; 021import java.io.StringReader; 022import java.io.StringWriter; 023import java.net.URL; 024import java.net.URLConnection; 025import java.util.ArrayList; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Properties; 029import java.util.regex.Matcher; 030import java.util.regex.Pattern; 031 032import org.apache.commons.csv.CSVFormat; 033import org.apache.commons.csv.CSVParser; 034import org.apache.commons.csv.CSVPrinter; 035import org.apache.commons.csv.CSVRecord; 036import org.apache.commons.csv.QuoteMode; 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039 040import org.nuxeo.common.utils.FileUtils; 041import org.nuxeo.theme.formats.styles.Style; 042 043import com.phloc.css.CCSS; 044import com.phloc.css.ECSSVersion; 045import com.phloc.css.ICSSWriterSettings; 046import com.phloc.css.decl.CSSDeclaration; 047import com.phloc.css.decl.CSSSelector; 048import com.phloc.css.decl.CSSStyleRule; 049import com.phloc.css.decl.CascadingStyleSheet; 050import com.phloc.css.reader.CSSReader; 051import com.phloc.css.writer.CSSWriterSettings; 052 053public final class Utils { 054 055 private static final Log log = LogFactory.getLog(Utils.class); 056 057 private static final String EMPTY_CSS_SELECTOR = "EMPTY"; 058 059 private static final Pattern emptyCssSelectorPattern = Pattern.compile("(.*?)\\{(.*?)\\}", Pattern.DOTALL); 060 061 private Utils() { 062 // This class is not supposed to be instantiated. 063 } 064 065 public static String listToCsv(List<String> list) { 066 StringWriter sw = new StringWriter(); 067 try (CSVPrinter writer = new CSVPrinter(sw, CSVFormat.DEFAULT.withDelimiter(',').withQuoteMode(QuoteMode.ALL))) { 068 writer.printRecord(list); 069 } catch (IOException e) { 070 log.error(e.getMessage(), e); 071 } 072 return sw.toString(); 073 } 074 075 public static List<String> csvToList(String str) throws IOException { 076 if ("".equals(str) || str == null) { 077 return new ArrayList<>(); 078 } 079 StringReader sr = new StringReader(str); 080 try (CSVParser reader = new CSVParser(sr, CSVFormat.DEFAULT.withDelimiter(','))) { 081 Iterator<CSVRecord> iterator = reader.iterator(); 082 if (!iterator.hasNext()) { 083 return new ArrayList<>(); 084 } else { 085 CSVRecord nextRecord = iterator.next(); 086 List<String> result = new ArrayList<>(nextRecord.size()); 087 for (String value : nextRecord) { 088 result.add(value); 089 } 090 return result; 091 } 092 } 093 } 094 095 public static boolean contains(final String[] array, final String value) { 096 for (String s : array) { 097 if (s.equals(value)) { 098 return true; 099 } 100 } 101 return false; 102 } 103 104 public static String cleanUp(String text) { 105 return text.replaceAll("\n", " ").replaceAll("\\t+", " ").replaceAll("\\s+", " ").trim(); 106 } 107 108 public static byte[] readResourceAsBytes(final String path) throws IOException { 109 return readResource(path).toByteArray(); 110 } 111 112 public static String readResourceAsString(final String path) throws IOException { 113 return readResource(path).toString(); 114 } 115 116 private static ByteArrayOutputStream readResource(final String path) throws IOException { 117 InputStream is = null; 118 ByteArrayOutputStream os = null; 119 try { 120 is = ResourceResolver.getInstance().getResourceAsStream(path); 121 if (is == null) { 122 log.warn("Resource not found: " + path); 123 } else { 124 try { 125 os = new ByteArrayOutputStream(); 126 byte[] buffer = new byte[1024]; 127 int i; 128 while ((i = is.read(buffer)) != -1) { 129 os.write(buffer, 0, i); 130 } 131 os.flush(); 132 } finally { 133 if (os != null) { 134 os.close(); 135 } 136 } 137 } 138 } finally { 139 if (is != null) { 140 try { 141 is.close(); 142 } finally { 143 is = null; 144 } 145 } 146 } 147 return os; 148 } 149 150 public static byte[] fetchUrl(URL url) { 151 byte[] data = null; 152 try { 153 final InputStream in = url.openStream(); 154 final ByteArrayOutputStream os = new ByteArrayOutputStream(); 155 byte[] buffer = new byte[1024]; 156 int i; 157 while ((i = in.read(buffer)) != -1) { 158 os.write(buffer, 0, i); 159 } 160 data = os.toByteArray(); 161 in.close(); 162 os.close(); 163 } catch (IOException e) { 164 log.error("Could not retrieve URL: " + url.toString()); 165 } 166 return data; 167 } 168 169 public static void writeFile(URL url, String text) throws IOException { 170 // local file system 171 if (url.getProtocol().equals("file")) { 172 String filepath = url.getFile(); 173 File file = new File(filepath); 174 FileUtils.writeFile(file, text); 175 } else { 176 OutputStream os = null; 177 URLConnection urlc; 178 try { 179 urlc = url.openConnection(); 180 os = urlc.getOutputStream(); 181 } catch (IOException e) { 182 log.error(e.getMessage(), e); 183 } 184 185 if (os != null) { 186 try { 187 os.write(text.getBytes()); 188 os.flush(); 189 } catch (IOException e) { 190 log.error(e.getMessage(), e); 191 } finally { 192 try { 193 os.close(); 194 } catch (IOException e) { 195 log.error(e.getMessage(), e); 196 } finally { 197 os = null; 198 } 199 } 200 } 201 202 } 203 204 } 205 206 public static void loadProperties(final Properties properties, final String resourceName) { 207 if (properties.isEmpty()) { 208 InputStream in = null; 209 try { 210 in = Utils.class.getResourceAsStream(resourceName); 211 if (in != null) { 212 properties.load(in); 213 } 214 } catch (IOException e) { 215 log.error("Could not load properties", e); 216 } finally { 217 if (in != null) { 218 try { 219 in.close(); 220 } catch (IOException e) { 221 log.error("Failed to close stream", e); 222 } 223 } 224 } 225 } 226 } 227 228 /** 229 * Parses and loads CSS resources into given style element. If boolean merge is set to true, keep existing 230 * properties already defined in style. 231 * 232 * @since 5.5 233 */ 234 public static void loadCss(final Style style, String cssSource, final String viewName, final boolean merge) { 235 // pre-processing: replace empty selectors (which are invalid 236 // selectors) with a marker selector 237 238 // replace ${basePath} occurrences with a temporary marker to avoid 239 // interfering with the regexp logic 240 cssSource = cssSource.replaceAll("\\$\\{basePath\\}", "TEMPORARY_BASE_PATH_MARKER"); 241 final Matcher matcher = emptyCssSelectorPattern.matcher(cssSource); 242 final StringBuilder buf = new StringBuilder(); 243 while (matcher.find()) { 244 if (matcher.group(1).trim().equals("")) { 245 buf.append(EMPTY_CSS_SELECTOR); 246 } 247 buf.append(matcher.group(0)); 248 } 249 cssSource = buf.toString(); 250 cssSource = cssSource.replaceAll("TEMPORARY_BASE_PATH_MARKER", "\\$\\{basePath\\}"); 251 252 CascadingStyleSheet styleSheet = CSSReader.readFromString(cssSource, "utf-8", ECSSVersion.CSS30); 253 if (styleSheet == null) { 254 log.error("Could not parse CSS:\n" + cssSource); 255 return; 256 } 257 258 if (!merge) { 259 // remove existing properties 260 style.clearPropertiesFor(viewName); 261 } 262 263 ICSSWriterSettings writerSettings = new CSSWriterSettings(ECSSVersion.CSS30, true); 264 for (CSSStyleRule rule : styleSheet.getAllStyleRules()) { 265 Properties properties = new Properties(); 266 for (CSSDeclaration declaration : rule.getAllDeclarations()) { 267 String expression = declaration.getExpression().getAsCSSString(writerSettings, 0); 268 if (declaration.isImportant()) { 269 expression = expression + CCSS.IMPORTANT_SUFFIX; 270 } 271 properties.put(declaration.getProperty(), expression); 272 } 273 274 for (CSSSelector cssSelector : rule.getAllSelectors()) { 275 String selector = cssSelector.getAsCSSString(writerSettings, 0); 276 if (selector.equals(EMPTY_CSS_SELECTOR) || selector.toLowerCase().equals("body") 277 || selector.toLowerCase().equals("html")) { 278 selector = ""; 279 } 280 style.setPropertiesFor(viewName, selector, properties); 281 } 282 } 283 } 284 285 public static void loadCss(final Style style, String cssSource, final String viewName) { 286 loadCss(style, cssSource, viewName, false); 287 } 288}