001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.markup.parser.filter; 018 019import java.text.ParseException; 020import java.util.Locale; 021 022import org.apache.wicket.markup.ComponentTag; 023import org.apache.wicket.markup.MarkupElement; 024import org.apache.wicket.markup.WicketParseException; 025import org.apache.wicket.markup.parser.AbstractMarkupFilter; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029 030/** 031 * This is a markup inline filter which by default is not added to the list of markup filter. It can 032 * be added by means of subclassing Application.newMarkupParser() like 033 * 034 * <pre> 035 * Application#init() { 036 * getMarkupSettings().setMarkupParserFactory() { 037 * new MarkupParserFactory() { 038 * MarkupParser newMarkupParser(final MarkupResourceStream resource) { 039 * MarkupParser parser=super.newMarkupParser(resource); 040 * parser.appendMarkupFilter(new HtmlProblemFinder(HtmlProblemFinder.ERR_THROW_EXCEPTION)); 041 * return parser; 042 * } 043 * } 044 * } 045 * } 046 * </pre> 047 * 048 * The purpose of the filter is to find possible HTML issues and to log a warning. 049 * 050 * @author Juergen Donnerstag 051 */ 052public final class HtmlProblemFinder extends AbstractMarkupFilter 053{ 054 /** Logging */ 055 private static final Logger log = LoggerFactory.getLogger(HtmlProblemFinder.class); 056 057 /** Ignore the issue detected */ 058 public static final int ERR_INGORE = 3; 059 060 /** Log a warning on the issue detected */ 061 public static final int ERR_LOG_WARN = 2; 062 063 /** Log an error on the issue detected */ 064 public static final int ERR_LOG_ERROR = 1; 065 066 /** Throw an exception on the issue detected */ 067 public static final int ERR_THROW_EXCEPTION = 0; 068 069 /** Default behavior in case of a potential HTML issue detected */ 070 private final int problemEscalation; 071 072 /** 073 * Construct. 074 * 075 * @param problemEscalation 076 * How to escalate the issue found. 077 */ 078 public HtmlProblemFinder(final int problemEscalation) 079 { 080 this.problemEscalation = problemEscalation; 081 } 082 083 @Override 084 protected final MarkupElement onComponentTag(ComponentTag tag) throws ParseException 085 { 086 // Make sure some typical and may be tricky problems are detected and 087 // logged. 088 if ("img".equals(tag.getName()) && (tag.isOpen() || tag.isOpenClose())) 089 { 090 String src = tag.getAttributes().getString("src"); 091 if ((src != null) && (src.trim().length() == 0)) 092 { 093 escalateWarning("Attribute 'src' should not be empty. Location: ", tag); 094 } 095 } 096 097 // Some people are using a dot "wicket.xxx" instead of a colon 098 // "wicket:xxx" 099 for (String key : tag.getAttributes().keySet()) 100 { 101 if (key != null) 102 { 103 key = key.toLowerCase(Locale.ROOT); 104 String namespaceDot = getWicketNamespace() + '.'; 105 if (key.startsWith(namespaceDot)) 106 { 107 escalateWarning( 108 String.format("You probably want '%s:xxx' rather than '%s.xxx'. Location: ", namespaceDot, namespaceDot), 109 tag); 110 } 111 } 112 } 113 114 return tag; 115 } 116 117 /** 118 * Handle the issue. Depending the setting either log a warning, an error, throw an exception or 119 * ignore it. 120 * 121 * @param msg 122 * The message 123 * @param tag 124 * The current tag 125 * @throws WicketParseException 126 */ 127 private void escalateWarning(final String msg, final ComponentTag tag) 128 throws WicketParseException 129 { 130 if (problemEscalation == ERR_LOG_WARN) 131 { 132 log.warn(msg + tag.toUserDebugString()); 133 } 134 else if (problemEscalation == ERR_LOG_ERROR) 135 { 136 log.error(msg + tag.toUserDebugString()); 137 } 138 else if (problemEscalation == ERR_INGORE) 139 { 140 // no action required 141 } 142 else 143 // if (problemEscalation == ERR_THROW_EXCEPTION) 144 { 145 throw new WicketParseException(msg, tag); 146 } 147 } 148}