001/* 002 * Copyright 2011 Anyware Services 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 */ 016package org.ametys.skinfactory.parameters; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.io.OutputStream; 021import java.nio.charset.StandardCharsets; 022import java.nio.file.Files; 023import java.nio.file.Path; 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030 031import org.apache.cocoon.xml.AttributesImpl; 032import org.apache.cocoon.xml.XMLUtils; 033import org.apache.commons.io.IOUtils; 034import org.xml.sax.ContentHandler; 035import org.xml.sax.SAXException; 036 037import org.ametys.runtime.i18n.I18nizableText; 038import org.ametys.web.skin.SkinModel; 039 040/** 041 * Implementation of {@link AbstractSkinParameter} for a CSS property 042 */ 043public class CSSParameter extends AbstractSkinParameter 044{ 045 private final List<Path> _cssFiles; 046 private final String _cssProperty; 047 private final String _defaultValue; 048 049 /** 050 * Constructor 051 * @param id the unique id 052 * @param label the label 053 * @param description the description 054 * @param cssFile the css file 055 * @param cssProperty the css property 056 * @param defaultValue the default value 057 */ 058 public CSSParameter(String id, I18nizableText label, I18nizableText description, Path cssFile, String cssProperty, String defaultValue) 059 { 060 super(id, label, description); 061 _cssFiles = new ArrayList<>(); 062 _cssFiles.add(cssFile); 063 _cssProperty = cssProperty; 064 _defaultValue = defaultValue; 065 } 066 067 /** 068 * Constructor 069 * @param id the unique id 070 * @param label the label 071 * @param description the description 072 * @param cssFiles the css files 073 * @param cssProperty the css property 074 * @param defaultValue the default value 075 */ 076 public CSSParameter(String id, I18nizableText label, I18nizableText description, List<Path> cssFiles, String cssProperty, String defaultValue) 077 { 078 super(id, label, description); 079 _cssFiles = cssFiles; 080 _cssProperty = cssProperty; 081 _defaultValue = defaultValue; 082 } 083 084 @Override 085 public SkinParameterType getType() 086 { 087 return SkinParameterType.CSS; 088 } 089 090 /** 091 * Get the CSS files path 092 * @return the CSS files path 093 */ 094 public List<Path> getCSSFiles () 095 { 096 return _cssFiles; 097 } 098 099 /** 100 * Add a CSS file 101 * @param cssFile The CSS file 102 */ 103 public void addCSSFile (Path cssFile) 104 { 105 for (Path f : _cssFiles) 106 { 107 if (f.normalize().toString().equals(cssFile.normalize().toString())) 108 { 109 // The file is already registered 110 return; 111 } 112 } 113 _cssFiles.add(cssFile); 114 } 115 116 /** 117 * Get the CSS property 118 * @return the CSS file path 119 */ 120 public String getCSSProperty () 121 { 122 return _cssProperty; 123 } 124 125 @Override 126 public void apply(Path tempDir, Path modelDir, Object value, String lang) throws SkinParameterException 127 { 128 for (Path file : _cssFiles) 129 { 130 String relPath = modelDir.relativize(file).toString(); 131 Path cssFile = tempDir.resolve(relPath); 132 String string = cssFileToString(cssFile); 133 134 Pattern pattern = getCSSPattern(); 135 Matcher matcher = pattern.matcher(string); 136 137 StringBuffer sb = new StringBuffer(); 138 int startIndex = 0; 139 while (matcher.find()) 140 { 141 int propertyStart = matcher.start(2); 142 143 String beforeValue = string.substring(startIndex, propertyStart); 144 String oldValue = string.substring(propertyStart, matcher.end(2)); 145 sb.append(beforeValue); 146 147 sb.append((String) value + (oldValue.endsWith(" ") ? " " : "")); 148 sb.append(string.substring(matcher.end(2), matcher.end(3))); 149 150 startIndex = matcher.end(3); 151 } 152 153 sb.append(string.substring(startIndex)); 154 155 try (OutputStream os = Files.newOutputStream(cssFile)) 156 { 157 IOUtils.write(sb.toString(), os, StandardCharsets.UTF_8); 158 } 159 catch (IOException e) 160 { 161 throw new SkinParameterException ("Unable to apply css parameter '" + getId() + "'", e); 162 } 163 } 164 } 165 166 /** 167 * Get the CSS pattern 168 * @return The CSS pattern 169 */ 170 protected Pattern getCSSPattern () 171 { 172 return Pattern.compile("\\s*([^,:\\s]*)\\s*:\\s*([^:;!]*)\\s*(?:!important)?\\s*\\/\\*\\s*AMETYS\\s*\\(\\s*\"(" + getId() + ")\"\\s*(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?(?:,\\s*([^,\"\\s]+|\"[^\"]*\")\\s*)?\\)\\s*\\*\\/\\s*;\\s*?", Pattern.MULTILINE); 173 } 174 175 /** 176 * Get the css file input stream as String 177 * @param cssFile The css file 178 * @return The css file content as String 179 * @throws SkinParameterException if the css file was not parsable 180 */ 181 protected String cssFileToString (Path cssFile) throws SkinParameterException 182 { 183 try (InputStream is = Files.newInputStream(cssFile)) 184 { 185 return org.apache.commons.io.IOUtils.toString(is, StandardCharsets.UTF_8); 186 } 187 catch (IOException e) 188 { 189 throw new SkinParameterException ("Unable to parse file '" + cssFile.getFileName().toString() + "'", e); 190 } 191 } 192 193 @Override 194 public void toSAX(ContentHandler contentHandler, String modelName) throws SAXException 195 { 196 AttributesImpl attrs = new AttributesImpl(); 197 attrs.addCDATAAttribute("id", getId()); 198 attrs.addCDATAAttribute("type", SkinParameterType.CSS.name().toLowerCase()); 199 200 XMLUtils.startElement(contentHandler, "parameter", attrs); 201 202 getLabel().toSAX(contentHandler, "label"); 203 getDescription().toSAX(contentHandler, "description"); 204 XMLUtils.createElement(contentHandler, "property", getCSSProperty()); 205 206 XMLUtils.endElement(contentHandler, "parameter"); 207 } 208 209 @Override 210 public Map<String, Object> toJson(String modelName) 211 { 212 Map<String, Object> jsonObject = new HashMap<>(); 213 214 jsonObject.put("id", getId()); 215 jsonObject.put("type", SkinParameterType.CSS.name().toLowerCase()); 216 jsonObject.put("label", getLabel()); 217 jsonObject.put("description", getDescription()); 218 jsonObject.put("property", getCSSProperty()); 219 220 return jsonObject; 221 } 222 223 @Override 224 public String getDefaultValue(SkinModel model) 225 { 226 return _defaultValue; 227 } 228 229 @Override 230 public String getDefaultValue(SkinModel model, String lang) 231 { 232 return getDefaultValue(model); 233 } 234}