/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.modules.cp;
import org.olat.core.util.StringHelper;
import org.olat.core.util.WebappHelper;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.VFSLeaf;
import org.w3c.css.sac.CSSException;
import org.w3c.css.sac.DocumentHandler;
import org.w3c.css.sac.InputSource;
import org.w3c.css.sac.LexicalUnit;
import org.w3c.css.sac.SACMediaList;
import org.w3c.css.sac.Selector;
import org.w3c.css.sac.SelectorList;
/**
*
* Description:<br>
* Rewrite relative path in the CSS. this is a document handler for
* a SAC Parser
*
* <P>
* Initial Date: 18 mars 2011 <br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
//fxdiff VCRP-14: print cp
public class SACCSSHandler implements DocumentHandler {
private boolean isInline;
private boolean selectorOpen;
private StringBuilder styleSheet = new StringBuilder();
private final String relativePath;
private final String baseUri;
private final VFSContainer rootContainer;
public SACCSSHandler(VFSLeaf document, VFSContainer rootContainer, String baseUri) {
this.baseUri = baseUri;
this.rootContainer = rootContainer;
relativePath = getRelativeResultingPath(document.getParentContainer());
}
public String getCleanStylesheet() {
// Always ensure results contain most recent generation of stylesheet
return styleSheet.toString();
}
@Override
public void comment(String comment) throws CSSException {
//
}
@Override
public void startDocument(InputSource source) throws CSSException {
// no-op
}
@Override
public void endDocument(InputSource source) throws CSSException {
// no-op
}
@Override
public void importStyle(String uri, SACMediaList media, String defaultNamespaceURI) throws CSSException {
//
}
@Override
public void startFontFace() throws CSSException {
// CSS2 Font Face declaration - ignore this for now
}
@Override
public void endFontFace() throws CSSException {
// CSS2 Font Face declaration - ignore this for now
}
@Override
public void startMedia(SACMediaList media) throws CSSException {
// CSS2 Media declaration - ignore this for now
}
@Override
public void endMedia(SACMediaList media) throws CSSException {
// CSS2 Media declaration - ignore this for now
}
@Override
public void startPage(String name, String pseudo_page) throws CSSException {
// CSS2 Page declaration - ignore this for now
}
@Override
public void endPage(String name, String pseudo_page) throws CSSException {
// CSS2 Page declaration - ignore this for now
}
@Override
public void startSelector(SelectorList selectors) throws CSSException {
// keep track of number of valid selectors from this rule
int selectorCount = 0;
// check each selector
for (int i = 0; i < selectors.getLength(); i++) {
Selector selector = selectors.item(i);
if (selector != null) {
String selectorName = selector.toString();
if (selectorCount > 0) {
styleSheet.append(',');
styleSheet.append(' ');
}
styleSheet.append(selectorName);
selectorCount++;
}
}
// if and only if there were selectors that were valid, append
// appropriate open brace and set state to within selector
if (selectorCount > 0) {
styleSheet.append(' ');
styleSheet.append('{');
styleSheet.append('\n');
selectorOpen = true;
}
}
@Override
public void endSelector(SelectorList selectors) throws CSSException {
// if we are in a state within a selector, close brace
if (selectorOpen) {
styleSheet.append('}');
styleSheet.append('\n');
}
// reset state
selectorOpen = false;
}
@Override
public void property(String name, LexicalUnit value, boolean important) throws CSSException {
if (!selectorOpen && !isInline) {
return;
}
// validate the property
if (!isInline) {
styleSheet.append('\t');
}
styleSheet.append(name);
styleSheet.append(':');
// append all values
while (value != null) {
styleSheet.append(' ');
styleSheet.append(lexicalValueToString(value));
value = value.getNextLexicalUnit();
}
styleSheet.append(';');
if (!isInline) {
styleSheet.append('\n');
}
}
@Override
public void ignorableAtRule(String atRule) throws CSSException {
// this method is called when the parser hits an unrecognized
// @-rule. Like the page/media/font declarations, this is
// CSS2+ stuff
}
@Override
public void namespaceDeclaration(String prefix, String uri) throws CSSException {
// CSS3 - Namespace declaration - ignore for now
}
/**
* Converts the given lexical unit to a <code>String</code>
* representation. This method does not perform any validation - it is meant
* to be used in conjunction with the validator/logging methods.
*
* @param lu
* the lexical unit to convert
* @return a <code>String</code> representation of the given lexical unit
*/
public String lexicalValueToString(LexicalUnit lu) {
switch (lu.getLexicalUnitType()) {
case LexicalUnit.SAC_PERCENTAGE:
case LexicalUnit.SAC_DIMENSION:
case LexicalUnit.SAC_EM:
case LexicalUnit.SAC_EX:
case LexicalUnit.SAC_PIXEL:
case LexicalUnit.SAC_INCH:
case LexicalUnit.SAC_CENTIMETER:
case LexicalUnit.SAC_MILLIMETER:
case LexicalUnit.SAC_POINT:
case LexicalUnit.SAC_PICA:
case LexicalUnit.SAC_DEGREE:
case LexicalUnit.SAC_GRADIAN:
case LexicalUnit.SAC_RADIAN:
case LexicalUnit.SAC_MILLISECOND:
case LexicalUnit.SAC_SECOND:
case LexicalUnit.SAC_HERTZ:
case LexicalUnit.SAC_KILOHERTZ:
// these are all measurements
return lu.getFloatValue() + lu.getDimensionUnitText();
case LexicalUnit.SAC_INTEGER:
// just a number
return String.valueOf(lu.getIntegerValue());
case LexicalUnit.SAC_REAL:
// just a number
return String.valueOf(lu.getFloatValue());
case LexicalUnit.SAC_STRING_VALUE:
case LexicalUnit.SAC_IDENT:
// just a string/identifier
String stringValue = lu.getStringValue();
if(stringValue.indexOf(" ") != -1)
stringValue = "\""+stringValue+"\"";
return stringValue;
case LexicalUnit.SAC_URI:
// this is a URL
return "url(" + normalizeUri(lu.getStringValue()) + ")";
case LexicalUnit.SAC_RGBCOLOR:
// this is a rgb encoded color
StringBuffer sb = new StringBuffer("rgb(");
LexicalUnit param = lu.getParameters();
sb.append(param.getIntegerValue()); // R value
sb.append(',');
param = param.getNextLexicalUnit(); // comma
param = param.getNextLexicalUnit(); // G value
sb.append(param.getIntegerValue());
sb.append(',');
param = param.getNextLexicalUnit(); // comma
param = param.getNextLexicalUnit(); // B value
sb.append(param.getIntegerValue());
sb.append(')');
return sb.toString();
case LexicalUnit.SAC_INHERIT:
// constant
return "inherit";
case LexicalUnit.SAC_OPERATOR_COMMA:
return ",";
case LexicalUnit.SAC_ATTR:
case LexicalUnit.SAC_COUNTER_FUNCTION:
case LexicalUnit.SAC_COUNTERS_FUNCTION:
case LexicalUnit.SAC_FUNCTION:
case LexicalUnit.SAC_RECT_FUNCTION:
case LexicalUnit.SAC_SUB_EXPRESSION:
case LexicalUnit.SAC_UNICODERANGE:
default:
// these are properties that shouldn't be necessary for most run
// of the mill HTML/CSS
return null;
}
}
private final String normalizeUri(String uri) {
if(uri.indexOf("://") > 0) {
return uri;//absolute link, nothing to do
}
String contextPath = WebappHelper.getServletContextPath();
if(uri.startsWith(contextPath)) {
return uri;//absolute within olat
}
if(uri.startsWith("..")) {
VFSContainer startDir;
if(relativePath == null) {
startDir = rootContainer;
} else {
startDir = (VFSContainer)rootContainer.resolve(relativePath);
}
String tmpUri = uri;
VFSContainer tmpDir = startDir;
while(tmpUri.startsWith("../")) {
tmpDir = tmpDir.getParentContainer();
tmpUri = tmpUri.substring(3);
}
String diffPath = getRelativeResultingPath(tmpDir);
if(StringHelper.containsNonWhitespace(diffPath)) {
return diffPath + tmpUri;
}
return tmpUri;
}
if (relativePath != null) {
uri = relativePath + uri;
}
return baseUri + "/" + uri;
}
private String getRelativeResultingPath(VFSItem tmpDir) {
String diffPath = "";
while(!tmpDir.isSame(rootContainer)) {
diffPath = tmpDir.getName() + "/" + diffPath;
tmpDir = tmpDir.getParentContainer();
}
return diffPath;
}
}