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