Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* A single CSS selector attribute.
*
* @see ECSSAttributeOperator
* @see ECSSAttributeCase
* @author Philip Helger
*/
@NotThreadSafe
Expand All @@ -44,6 +45,7 @@ public class CSSSelectorAttribute implements ICSSSelectorMember, ICSSSourceLocat
private final String m_sAttrName;
private final ECSSAttributeOperator m_eOperator;
private final String m_sAttrValue;
private final ECSSAttributeCase m_eAttrCase;
private CSSSourceLocation m_aSourceLocation;

private static boolean _isValidNamespacePrefix (@Nullable final String sNamespacePrefix)
Expand All @@ -61,12 +63,22 @@ public CSSSelectorAttribute (@Nullable final String sNamespacePrefix, @Nonnull @
m_sAttrName = sAttrName;
m_eOperator = null;
m_sAttrValue = null;
m_eAttrCase = null;
}

public CSSSelectorAttribute (@Nullable final String sNamespacePrefix,
@Nonnull @Nonempty final String sAttrName,
@Nonnull final ECSSAttributeOperator eOperator,
@Nonnull final String sAttrValue)
{
this (sNamespacePrefix, sAttrName, eOperator, sAttrValue, null);
}

public CSSSelectorAttribute (@Nullable final String sNamespacePrefix,
@Nonnull @Nonempty final String sAttrName,
@Nullable final ECSSAttributeOperator eOperator,
@Nullable final String sAttrValue,
@Nullable final ECSSAttributeCase eCaseFlag)
{
if (!_isValidNamespacePrefix (sNamespacePrefix))
throw new IllegalArgumentException ("namespacePrefix is illegal!");
Expand All @@ -78,6 +90,7 @@ public CSSSelectorAttribute (@Nullable final String sNamespacePrefix,
m_sAttrName = sAttrName;
m_eOperator = eOperator;
m_sAttrValue = sAttrValue;
m_eAttrCase = eCaseFlag;
}

@Nullable
Expand Down Expand Up @@ -105,6 +118,12 @@ public String getAttrValue ()
return m_sAttrValue;
}

@Nullable
public ECSSAttributeCase getCaseSensitivityFlag ()
{
return m_eAttrCase;
}

@Nonnull
@Nonempty
public String getAsCSSString (@Nonnull final ICSSWriterSettings aSettings, @Nonnegative final int nIndentLevel)
Expand All @@ -115,7 +134,11 @@ public String getAsCSSString (@Nonnull final ICSSWriterSettings aSettings, @Nonn
aSB.append (m_sNamespacePrefix);
aSB.append (m_sAttrName);
if (m_eOperator != null)
{
aSB.append (m_eOperator.getAsCSSString (aSettings, nIndentLevel)).append (m_sAttrValue);
if (m_eAttrCase != null)
aSB.append (' ').append (m_eAttrCase.getName ());
}
return aSB.append (']').toString ();
}

Expand All @@ -141,7 +164,8 @@ public boolean equals (final Object o)
return EqualsHelper.equals (m_sNamespacePrefix, rhs.m_sNamespacePrefix) &&
m_sAttrName.equals (rhs.m_sAttrName) &&
EqualsHelper.equals (m_eOperator, rhs.m_eOperator) &&
EqualsHelper.equals (m_sAttrValue, rhs.m_sAttrValue);
EqualsHelper.equals (m_sAttrValue, rhs.m_sAttrValue) &&
EqualsHelper.equals (m_eAttrCase, rhs.m_eAttrCase);
}

@Override
Expand All @@ -151,6 +175,7 @@ public int hashCode ()
.append (m_sAttrName)
.append (m_eOperator)
.append (m_sAttrValue)
.append (m_eAttrCase)
.getHashCode ();
}

Expand All @@ -161,6 +186,7 @@ public String toString ()
.append ("attrName", m_sAttrName)
.appendIfNotNull ("operator", m_eOperator)
.appendIfNotNull ("attrValue", m_sAttrValue)
.appendIfNotNull ("caseFlag", m_eAttrCase)
.appendIfNotNull ("SourceLocation", m_aSourceLocation)
.getToString ();
}
Expand Down
62 changes: 62 additions & 0 deletions ph-css/src/main/java/com/helger/css/decl/ECSSAttributeCase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2014-2025 Philip Helger (www.helger.com)
* philip[at]helger[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.helger.css.decl;

import com.helger.annotation.Nonempty;
import com.helger.annotation.Nonnegative;
import com.helger.base.lang.EnumHelper;
import com.helger.base.name.IHasName;
import com.helger.css.ICSSWriteable;
import com.helger.css.ICSSWriterSettings;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;

/**
* Enumeration with case sensitivity flags as in
* <code>[foo="bar" i]</code> or <code>[foo="bar" s]</code>
*
* @author Aditi Singh
*/

public enum ECSSAttributeCase implements ICSSWriteable, IHasName {
CASE_SENSITIVE("s"),
CASE_INSENSITIVE("i");

private final String m_sName;

ECSSAttributeCase(@Nonnull @Nonempty final String sName) {
m_sName = sName;
}

@Nonnull
@Nonempty
public String getName() {
return m_sName;
}

@Nonnull
@Nonempty
public String getAsCSSString(@Nonnull final ICSSWriterSettings aSettings, @Nonnegative final int nIndentLevel) {
return m_sName;
}

@Nullable
public static ECSSAttributeCase getFromNameOrNull(@Nullable final String sName) {
return EnumHelper.getFromNameOrNull(ECSSAttributeCase.class, sName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,17 @@ private CSSSelectorAttribute _createSelectorAttribute (@Nonnull final CSSNode aN
}
else
{
final int nExpectedChildCount = nOperatorIndex + 2;
if (nChildren != nExpectedChildCount)
// With operator and value
int nMin = nOperatorIndex + 2;
// With operator, value and case sensitivity flag
int nMax = nOperatorIndex + 3;

if (nChildren < nMin || nChildren > nMax)
_throwUnexpectedChildrenCount (aNode,
"Illegal number of children present (" +
nChildren +
") - expected " +
nExpectedChildCount);
") - expected it to be more than " +
nMin + " and less than " + nMax);

// With operator...
final CSSNode aOperator = aNode.jjtGetChild (nOperatorIndex);
Expand All @@ -182,10 +186,20 @@ private CSSSelectorAttribute _createSelectorAttribute (@Nonnull final CSSNode aN
final CSSNode aAttrValue = aNode.jjtGetChild (nOperatorIndex + 1);
_expectNodeType (aAttrValue, ECSSNodeType.ATTRIBVALUE);

ECSSAttributeCase eCaseFlag = null;
// Optional case sensitivity flag
if (nChildren == nMax)
{
final CSSNode aFlag = aNode.jjtGetChild (nOperatorIndex + 2);
_expectNodeType (aFlag, ECSSNodeType.ATTRIBCASE);
eCaseFlag = ECSSAttributeCase.getFromNameOrNull (aFlag.getText ());
}

ret = new CSSSelectorAttribute (sNamespacePrefix,
sAttrName,
ECSSAttributeOperator.getFromNameOrNull (aOperator.getText ()),
aAttrValue.getText ());
aAttrValue.getText (),
eCaseFlag);
}
if (m_bUseSourceLocation)
ret.setSourceLocation (aNode.getSourceLocation ());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public enum ECSSNodeType
ATTRIB (ParserCSS30TreeConstants.JJTATTRIBUTESELECTOR),
ATTRIBOPERATOR (ParserCSS30TreeConstants.JJTATTRIBOPERATOR),
ATTRIBVALUE (ParserCSS30TreeConstants.JJTATTRIBVALUE),
ATTRIBCASE (ParserCSS30TreeConstants.JJTATTRIBCASE),
// RELATIVE_SELECTOR (ParserCSS30TreeConstants.JJTRELATIVESELECTOR),
SELECTORCOMBINATOR (ParserCSS30TreeConstants.JJTSELECTORCOMBINATOR),
NTH (ParserCSS30TreeConstants.JJTNTH),
Expand Down
17 changes: 17 additions & 0 deletions ph-css/src/main/jjtree/ParserCSS30.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,21 @@ void attribValue() :
)
}

void attribCase() : {}
{
( <S> )*
( <IDENT>
{
final String v = token.image;
if ("i".equalsIgnoreCase(v) || "s".equalsIgnoreCase(v))
jjtThis.setText(v.toLowerCase(java.util.Locale.ROOT));
else
throw new ParseException("Invalid case-sensitivity flag '" + v +
"'. Only 'i' or 's' are allowed.");
}
)?
}

void attributeSelector() : {}
{
<LSQUARE>
Expand All @@ -957,7 +972,9 @@ void attributeSelector() : {}
( <S> )*
attribValue()
( <S> )*
attribCase()
)?
( <S> )*
<RSQUARE>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void testRead1 ()
@Test
public void testRead2 ()
{
CSSStyleRule aSR = _parse ("div, .colored, #my-red, #menu > .active, a[href^=red] { }");
CSSStyleRule aSR = _parse ("div, .colored, #my-red, #menu > .active, a[href^=red i] { }");
assertEquals (5, aSR.getSelectorCount ());

assertEquals (1, aSR.getSelectorAtIndex (0).getMemberCount ());
Expand All @@ -94,6 +94,7 @@ public void testRead2 ()
assertEquals (2, aSR.getSelectorAtIndex (4).getMemberCount ());
assertTrue (aSR.getSelectorAtIndex (4).getMemberAtIndex (0) instanceof CSSSelectorSimpleMember);
assertTrue (aSR.getSelectorAtIndex (4).getMemberAtIndex (1) instanceof CSSSelectorAttribute);
assertEquals (ECSSAttributeCase.CASE_INSENSITIVE, ((CSSSelectorAttribute) aSR.getSelectorAtIndex (4).getMemberAtIndex (1)).getCaseSensitivityFlag ());

// Create the same rule by application
final CSSStyleRule aCreated = new CSSStyleRule ();
Expand All @@ -107,7 +108,8 @@ public void testRead2 ()
.addMember (new CSSSelectorAttribute (null,
"href",
ECSSAttributeOperator.BEGINMATCH,
"red")));
"red",
ECSSAttributeCase.CASE_INSENSITIVE)));
TestHelper.testDefaultImplementationWithEqualContentObject (aSR, aCreated);
}
}