
com.phloc.html.hc.conversion.HCConsistencyChecker Maven / Gradle / Ivy
/**
* Copyright (C) 2006-2015 phloc systems
* http://www.phloc.com
* office[at]phloc[dot]com
*
* 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.
*/
package com.phloc.html.hc.conversion;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.phloc.commons.annotations.PresentForCodeCoverage;
import com.phloc.commons.annotations.ReturnsMutableCopy;
import com.phloc.commons.cache.AnnotationUsageCache;
import com.phloc.commons.state.EFinish;
import com.phloc.commons.string.StringHelper;
import com.phloc.html.EHTMLElement;
import com.phloc.html.EHTMLVersion;
import com.phloc.html.annotations.DeprecatedInHTML4;
import com.phloc.html.annotations.DeprecatedInHTML5;
import com.phloc.html.annotations.DeprecatedInXHTML1;
import com.phloc.html.annotations.SinceHTML5;
import com.phloc.html.hc.IHCElement;
import com.phloc.html.hc.IHCHasChildren;
import com.phloc.html.hc.IHCNode;
import com.phloc.html.hc.html.AbstractHCBaseTable;
import com.phloc.html.hc.html.HCA;
import com.phloc.html.hc.html.HCBody;
import com.phloc.html.hc.html.HCButton;
import com.phloc.html.hc.html.HCForm;
import com.phloc.html.hc.html.HCObject;
import com.phloc.html.hc.html.HCPre;
import com.phloc.html.hc.html5.HCMeter;
import com.phloc.html.hc.html5.HCProgress;
import com.phloc.html.hc.htmlext.HCUtils;
import com.phloc.html.hc.htmlext.IHCIteratorCallback;
/**
* This class performs some consistency checks on HCNodes
*
* @author Philip Helger
*/
@Immutable
public final class HCConsistencyChecker
{
private static final Logger s_aLogger = LoggerFactory.getLogger (HCConsistencyChecker.class);
private static final AnnotationUsageCache s_aAUC_D_HTML4 = new AnnotationUsageCache (DeprecatedInHTML4.class);
private static final AnnotationUsageCache s_aAUC_D_XHTML1 = new AnnotationUsageCache (DeprecatedInXHTML1.class);
private static final AnnotationUsageCache s_aAUC_D_HTML5 = new AnnotationUsageCache (DeprecatedInHTML5.class);
private static final AnnotationUsageCache s_aAUC_S_HTML5 = new AnnotationUsageCache (SinceHTML5.class);
@SuppressWarnings ("unused")
@PresentForCodeCoverage
private static final HCConsistencyChecker s_aInstance = new HCConsistencyChecker ();
private HCConsistencyChecker ()
{}
public static void consistencyAssert (final boolean bCondition, final String sMsg)
{
if (!bCondition)
throw new IllegalStateException ("Consistency check failed: " + sMsg);
}
public static void consistencyWarning (final String sMsg)
{
s_aLogger.warn (sMsg);
}
private static void _checkDeprecation (final Class > aElementClass,
final String sElementName,
final EHTMLVersion eHTMLVersion)
{
if (s_aAUC_D_HTML4.hasAnnotation (aElementClass))
consistencyWarning ("The element '" + sElementName + "' was deprecated in HTML 4.0");
else
if (s_aAUC_D_XHTML1.hasAnnotation (aElementClass))
consistencyWarning ("The element '" + sElementName + "' is deprecated in XHTML1");
else
if (eHTMLVersion.isAtLeastHTML5 ())
{
// HTML5 specifics checks
if (s_aAUC_D_HTML5.hasAnnotation (aElementClass))
consistencyWarning ("The element '" + sElementName + "' is deprecated in HTML5");
}
else
{
// pre-HTML5 checks
if (s_aAUC_S_HTML5.hasAnnotation (aElementClass))
consistencyWarning ("The element '" + sElementName + "' is only available in HTML5");
}
}
private static void _checkA (final HCA aA)
{
if (HCUtils.recursiveContainsChildWithTagName (aA, EHTMLElement.A))
consistencyWarning ("A may never contain other links!");
if (HCUtils.recursiveContainsChildWithTagName (aA, EHTMLElement.SELECT))
consistencyWarning ("A contains invalid child element!");
}
private static void _checkButton (final HCButton aButton)
{
final IHCElement > aChild = HCUtils.recursiveGetFirstChildWithTagName (aButton,
EHTMLElement.A,
EHTMLElement.INPUT,
EHTMLElement.SELECT,
EHTMLElement.TEXTAREA,
EHTMLElement.LABEL,
EHTMLElement.BUTTON,
EHTMLElement.FORM,
EHTMLElement.FIELDSET,
EHTMLElement.IFRAME);
if (aChild != null)
consistencyWarning ("BUTTON element contains forbidden tag " + aChild.getElement ());
}
private static void _checkForm (final HCForm aForm)
{
if (HCUtils.recursiveContainsChildWithTagName (aForm, EHTMLElement.FORM))
consistencyWarning ("FORM contains other nested form");
}
private static void _checkMeter (final HCMeter aMeter)
{
if (HCUtils.recursiveContainsChildWithTagName (aMeter, EHTMLElement.METER))
consistencyWarning ("METER contains other nested meter");
}
private static void _checkObject (final HCObject aValue)
{
if (aValue.getData () == null && aValue.getType () == null)
consistencyWarning ("OBJECT contains neither type nor data");
}
private static void _checkPre (final HCPre aPre)
{
final IHCElement > aChild = HCUtils.recursiveGetFirstChildWithTagName (aPre,
EHTMLElement.IMG,
EHTMLElement.OBJECT,
EHTMLElement.SMALL,
EHTMLElement.SUB,
EHTMLElement.SUP);
if (aChild != null)
consistencyWarning ("PRE elements contains forbidden tag " + aChild.getElement ());
}
private static void _checkProgress (final HCProgress aProgress)
{
if (HCUtils.recursiveContainsChildWithTagName (aProgress, EHTMLElement.PROGRESS))
consistencyWarning ("PROGRESS contains other nested progress");
}
private static void _checkTable (@Nonnull final AbstractHCBaseTable > aTable)
{
AbstractHCBaseTable.checkInternalConsistency (aTable);
}
public static void runConsistencyCheckBeforeCreation (@Nonnull final IHCElement > aElement,
@Nonnull final EHTMLVersion eHTMLVersion)
{
final String sElementName = aElement.getTagName ();
final Class > aElementClass = aElement.getClass ();
// Deprecation is checked for all elements
_checkDeprecation (aElementClass, sElementName, eHTMLVersion);
// Special checks based on the implementation
if (aElement instanceof HCA)
_checkA ((HCA) aElement);
else
if (aElement instanceof HCButton)
_checkButton ((HCButton) aElement);
else
if (aElement instanceof HCForm)
_checkForm ((HCForm) aElement);
else
if (aElement instanceof HCMeter)
_checkMeter ((HCMeter) aElement);
else
if (aElement instanceof HCObject)
_checkObject ((HCObject) aElement);
else
if (aElement instanceof HCPre)
_checkPre ((HCPre) aElement);
else
if (aElement instanceof HCProgress)
_checkProgress ((HCProgress) aElement);
else
if (aElement instanceof AbstractHCBaseTable >)
_checkTable ((AbstractHCBaseTable >) aElement);
}
public static void checkIfLinkIsMasked (@Nullable final String sHref)
{
if (sHref != null)
{
// FIXME: this is potential vulnerability. If the passed href is passed
// from a user input, which cannot be told at this point, it might as well
// contain a'&' followed by some malicious code that should be
// escaped.
// Note PH: this is not a vulnerability. This is a programming error!
consistencyAssert (!sHref.contains ("&"), "The URL '" +
sHref +
"' seems to be already escaped - please use an unescaped URL!!");
}
}
/**
* Check all nodes inside the passed HTML body whether they have unique IDs or
* not.
*
* @param aBody
* The HTML body to check. May not be null
.
* @return A set with all IDs used more than once. Never null
but
* maybe empty.
*/
@Nonnull
@ReturnsMutableCopy
public static Set checkForUniqueIDs (@Nonnull final HCBody aBody)
{
final Set aUsedIDs = new HashSet ();
final Set aDuplicateIDs = new HashSet ();
HCUtils.iterateTree (aBody, new IHCIteratorCallback ()
{
@Nonnull
public EFinish call (@Nullable final IHCHasChildren aParentNode, @Nonnull final IHCNode aChildNode)
{
if (aChildNode instanceof IHCElement >)
{
final IHCElement > aElement = (IHCElement >) aChildNode;
final String sID = aElement.getID ();
if (StringHelper.hasText (sID) && !aUsedIDs.add (sID))
{
consistencyWarning ("The ID '" + sID + "' is used more than once within a single HTML page!");
aDuplicateIDs.add (sID);
}
}
return EFinish.UNFINISHED;
}
});
return aDuplicateIDs;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy