/*
* Copyright 2009 Richard Nichols.
*
* Licensed 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.
* under the License.
*/
package com.visural.wicket.component.codebox;
import com.google.prettify.ExtraJSResourceReference;
import com.google.prettify.PrettifyCSSResourceReference;
import com.google.prettify.PrettifyJSResourceReference;
import com.visural.wicket.security.IPrivilege;
import com.visural.wicket.security.ISecureEnableInstance;
import com.visural.wicket.security.ISecureRenderInstance;
import java.io.Serializable;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.JavascriptPackageResource;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
/**
* A code block which does syntax highlighting of the code contents.
*
* Apply to `<pre></pre>` or `<code></code>` DOM element.
*
* Wraps the Prettify library by Google - http://code.google.com/p/google-code-prettify/
*
* It is not necessary to specify a language, prettify will guess the language
* based on content, however an override is available should it be required.
*
* Apply to a `<pre>` or `<code>` block.
*
* @version $Id: CodeBox.java 232 2010-11-22 09:51:32Z tibes80@gmail.com $
* @author Richard Nichols
*/
public class CodeBox extends WebComponent implements Serializable, ISecureEnableInstance, ISecureRenderInstance {
private static final long serialVersionUID = 1L;
private boolean displayLineNumbers = false;
private CodeBoxLanguage languageOverride = null;
/**
* Create a Codebox with static content with the given `id`.
* @param id
*/
public CodeBox(String id) {
super(id);
includeHeaderContributions();
}
/**
* Create a Codebox with the provided code content and the given `id`.
* @param id
* @param code source code to display
*/
public CodeBox(final String id, String code) {
this(id, new Model(code));
}
/**
* Create a codebox with source code provided by an `IModel` and the given `id`.
* @param id
* @param model a model that will provide the source code to display
*/
public CodeBox(final String id, IModel model) {
super(id, model);
includeHeaderContributions();
}
/**
* Override and return false to suppress static Javascript and CSS contributions.
* (May be desired if you are concatenating / compressing resources as part of build process)
* @return
*/
protected boolean autoAddToHeader() {
return true;
}
private void includeHeaderContributions() {
if (autoAddToHeader()) {
add(new HeaderContributor(CSSPackageResource.getHeaderContribution(new PrettifyCSSResourceReference())));
add(new HeaderContributor(JavascriptPackageResource.getHeaderContribution(new PrettifyJSResourceReference())));
}
add(new HeaderContributor(new IHeaderContributor() {
public void renderHead(IHeaderResponse response) {
if (getLanguageOverride() != null && getLanguageOverride().getExtraJSfile() != null) {
response.renderJavascriptReference(new ExtraJSResourceReference(getLanguageOverride()));
}
response.renderOnDomReadyJavascript("prettyPrint()");
}
}));
}
@Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
// check applied to code/pre (need to bring some code in from parent as can apply to either)
if (!tag.getName().equalsIgnoreCase("pre") && !tag.getName().equalsIgnoreCase("code")) {
findMarkupStream().throwMarkupException(
"Component " + getId() + " must be applied to a tag of type 'code' or 'pre', not " + tag.toUserDebugString());
}
// change display class
if (getLanguageOverride() == null) {
tag.put("class", "prettyprint");
} else {
tag.put("class", "prettyprint " + getLanguageOverride().getCSSClass());
}
}
@Override
protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
String code = this.getDefaultModelObjectAsString();
if (code != null) {
if (isDisplayLineNumbers()) {
code = formatLineNumbers(code);
}
replaceComponentTagBody(markupStream, openTag, code);
} else {
super.onComponentTagBody(markupStream, openTag);
}
}
private String formatLineNumbers(String code) {
StringBuffer codeWithLines = new StringBuffer(code.length() * 2);
String[] lines = code.split("\n");
int numPlaces = Integer.toString(lines.length).length();
int lineNo = 1;
for (String line : lines) {
codeWithLines.append("<span class=\"nocode\">");
codeWithLines.append(rightJustifyAndPad(lineNo++, numPlaces));
codeWithLines.append(":</span> ");
codeWithLines.append(line);
codeWithLines.append('\n');
}
code = codeWithLines.toString();
return code;
}
public boolean isDisplayLineNumbers() {
return displayLineNumbers;
}
/**
* Toggle the display of line numbers in the left gutter.
*
* @param displayLineNumbers
* @return
*/
public CodeBox setDisplayLineNumbers(boolean displayLineNumbers) {
this.displayLineNumbers = displayLineNumbers;
return this;
}
public CodeBoxLanguage getLanguageOverride() {
return languageOverride;
}
/**
* Override the language used for syntax highligting.
*
* @param languageOverride
* @return
*/
public CodeBox setLanguageOverride(CodeBoxLanguage languageOverride) {
this.languageOverride = languageOverride;
return this;
}
private String rightJustifyAndPad(int lineNo, int places) {
StringBuffer result = new StringBuffer(places);
result.append(lineNo);
while (result.length() < places) {
result.insert(0, ' ');
}
return result.toString();
}
public IPrivilege getRenderPrivilege() {
return IPrivilege.NULL;
}
public IPrivilege getEnablePrivilege() {
return IPrivilege.NULL;
}
}