blob: 77b8afdcd29ea51db57a6d5ff35a40b405cb4277 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hyracks.maven.license.freemarker;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.Map;
import freemarker.core.Environment;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import org.apache.commons.io.IOUtils;
import org.apache.hyracks.maven.license.LicenseUtil;
public class IndentDirective implements TemplateDirectiveModel {
private static final String PARAM_NAME_SPACES = "spaces";
private static final String PARAM_NAME_UNPAD = "unpad";
private static final String PARAM_NAME_WRAP = "wrap";
@Override
public void execute(Environment env, Map params, TemplateModel [] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
int numSpaces = -1;
boolean unpad = false;
boolean wrap = false;
for (Object o : params.entrySet()) {
Map.Entry ent = (Map.Entry) o;
String paramName = (String) ent.getKey();
TemplateModel paramValue = (TemplateModel) ent.getValue();
switch (paramName) {
case PARAM_NAME_SPACES:
numSpaces = getIntParam(paramName, paramValue);
break;
case PARAM_NAME_UNPAD:
unpad = getBooleanParam(paramName, paramValue);
break;
case PARAM_NAME_WRAP:
wrap = getBooleanParam(paramName, paramValue);
break;
default:
throw new TemplateModelException("Unsupported parameter: " + paramName);
}
}
if (numSpaces < 0) {
throw new TemplateModelException("The required \"" + PARAM_NAME_SPACES + "\" parameter is missing.");
}
if (body == null) {
throw new TemplateModelException("Indent requires a body");
} else {
// Executes the nested body (same as <#nested> in FTL). In this
// case we don't provide a special writer as the parameter:
StringWriter sw = new StringWriter();
body.render(sw);
String fixedup = LicenseUtil.process(sw.toString(), unpad, wrap);
IOUtils.copy(new StringReader(fixedup), new IndentingWriter(env.getOut(), numSpaces));
}
}
private int getIntParam(String paramName, TemplateModel paramValue) throws TemplateModelException {
int spacesParam;
if (!(paramValue instanceof TemplateNumberModel)) {
throw paramException(paramName, "must be a number");
}
spacesParam = ((TemplateNumberModel) paramValue).getAsNumber().intValue();
if (spacesParam < 0) {
throw paramException(paramName, "can't be negative");
}
return spacesParam;
}
private boolean getBooleanParam(String paramName, TemplateModel paramValue) throws TemplateModelException {
if (!(paramValue instanceof TemplateBooleanModel)) {
throw paramException(paramName, "must be a boolean");
}
return ((TemplateBooleanModel) paramValue).getAsBoolean();
}
private TemplateModelException paramException(String paramName, String message) throws TemplateModelException {
return new TemplateModelException(
"The '" + paramName + "' parameter " + message);
}
private static class IndentingWriter extends Writer {
private final Writer out;
private final char[] padChars;
boolean needsToPad;
public IndentingWriter(Writer out, int numSpaces) {
this.out = out;
padChars = new char[numSpaces];
Arrays.fill(padChars, ' ');
needsToPad = true;
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
for (int i = off; i < len; i++) {
if (cbuf[i] == '\n') {
out.write(cbuf[i]);
needsToPad = true;
} else {
if (needsToPad) {
out.write(padChars);
needsToPad = false;
}
out.write(cbuf[i]);
}
}
}
@Override
public void flush() throws IOException {
out.flush();
}
@Override
public void close() throws IOException {
out.flush();
}
}
}