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