org.netbeans.modules.web.inspect.CSSUtils Maven / Gradle / Ivy
The newest version!
/*
* 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.netbeans.modules.web.inspect;
import java.awt.EventQueue;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.text.BadLocationException;
import javax.swing.text.StyledDocument;
import org.netbeans.modules.css.model.api.Model;
import org.netbeans.modules.web.common.api.LexerUtils;
import org.netbeans.modules.web.common.sourcemap.Mapping;
import org.netbeans.modules.web.common.sourcemap.SourceMap;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.cookies.EditorCookie;
import org.openide.cookies.LineCookie;
import org.openide.cookies.OpenCookie;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.text.Line;
import org.openide.text.NbDocument;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.UserQuestionException;
/**
* CSS-related utility methods.
*
* @author Jan Stola
*/
public class CSSUtils {
/** Prefixes of CSS properties used by various vendors. */
private static final String[] vendorPropertyPrefixes = new String[] {
"-moz-", // NOI18N
"-webkit-", // NOI18N
"-ms-", // NOI18N
"-o-" // NOI18N
};
private static final List inheritedProperties = Arrays.asList(new String[] {
"azimuth", // NOI18N
"border-collapse", // NOI18N
"border-spacing", // NOI18N
"caption-side", // NOI18N
"color", // NOI18N
"cursor", // NOI18N
"direction", // NOI18N
"elevation", // NOI18N
"empty-cells", // NOI18N
"font-family", // NOI18N
"font-size", // NOI18N
"font-style", // NOI18N
"font-variant", // NOI18N
"font-weight", // NOI18N
"font", // NOI18N
"letter-spacing", // NOI18N
"line-height", // NOI18N
"list-style-image", // NOI18N
"list-style-position", // NOI18N
"list-style-type", // NOI18N
"list-style", // NOI18N
"orphans", // NOI18N
"pitch-range", // NOI18N
"pitch", // NOI18N
"quotes", // NOI18N
"richness", // NOI18N
"speak-header", // NOI18N
"speak-numeral", // NOI18N
"speak-punctuation", // NOI18N
"speak", // NOI18N
"speech-rate", // NOI18N
"stress", // NOI18N
"text-align", // NOI18N
"text-indent", // NOI18N
"text-transform", // NOI18N
"text-shadow", // NOI18N
"visibility", // NOI18N
"voice-family", // NOI18N
"volume", // NOI18N
"white-space", // NOI18N
"widows", // NOI18N
"word-spacing" // NOI18N
});
/** Name of the class that is used to simulate hovering. */
public static final String HOVER_CLASS = "-netbeans-hover"; // NOI18N
/**
* Determines whether the CSS property with the specified name is inherited.
*
* @param name name of the property.
* @return {@code true} when the property is inherited,
* returns {@code false} otherwise.
*/
public static boolean isInheritedProperty(String name) {
for (String propertyName : possiblePropertyNames(name)) {
if (inheritedProperties.contains(propertyName)) {
return true;
}
}
return false;
}
/**
* Returns possible known (base) names of the given property
* (the returned list contains all candidates with
* possible prefixes/suffixes removed).
*
* @param name name of the property.
* @return list of possible (base) names of the given property.
*/
private static List possiblePropertyNames(String name) {
List names = new ArrayList();
names.add(name);
// -moz-color => color
for (String prefix : vendorPropertyPrefixes) {
if (name.startsWith(prefix)) {
String withoutPrefix = name.substring(prefix.length());
names.add(withoutPrefix);
}
}
return names;
}
/**
* Determines whether the specified CSS value means that the actual
* value should be inherited from the parent.
*
* @param value value to check.
* @return {@code true} if the actual value should be inherited,
* returns {@code false} otherwise.
*/
public static boolean isInheritValue(String value) {
return value.trim().startsWith("inherit"); // NOI18N
}
/**
* Opens the specified file at the given offset.
*
* @param fob file that should be opened.
* @param offset offset where the caret should be placed.
* @return {@code true} when the file was opened successfully,
* returns {@code false} otherwise.
*/
public static boolean openAtOffset(FileObject fob, int offset) {
return openAt(fob, -1, offset);
}
/**
* Opens the specified file at the given line.
*
* @param fob file that should be opened.
* @param lineNo line where the caret should be placed.
* @return {@code true} when the file was opened successfully,
* returns {@code false} otherwise.
*/
public static boolean openAtLine(FileObject fob, int lineNo) {
return openAt(fob, lineNo, 0);
}
/**
* Opens the specified file at the given line and column.
*
* @param fob file that should be opened.
* @param lineNo line where the caret should be placed.
* @param columnNo column where the caret should be placed.
* @return {@code true} when the file was opened successfully,
* returns {@code false} otherwise.
*/
public static boolean openAtLineAndColumn(FileObject fob, int lineNo, int columnNo) {
return openAt(fob, lineNo, columnNo);
}
/**
* Opens the specified file at the given position. The position is either
* offset (when {@code lineNo} is -1) or line/column otherwise.
* This method has been copied (with minor modifications) from UiUtils
* class in csl.api module. This method is not CSS-specific. It was placed
* into this file just because there was no better place.
*
* @param fob file that should be opened.
* @param lineNo line where the caret should be placed
* (or -1 when the {@code columnNo} represents an offset).
* @param columnNo column (or offset when {@code lineNo} is -1)
* where the caret should be placed.
* @return {@code true} when the file was opened successfully,
* returns {@code false} otherwise.
*/
private static boolean openAt(FileObject fob, int lineNo, int columnNo) {
try {
DataObject dob = DataObject.find(fob);
Lookup dobLookup = dob.getLookup();
EditorCookie ec = dobLookup.lookup(EditorCookie.class);
LineCookie lc = dobLookup.lookup(LineCookie.class);
OpenCookie oc = dobLookup.lookup(OpenCookie.class);
if ((ec != null) && (lc != null) && (columnNo != -1)) {
StyledDocument doc;
try {
doc = ec.openDocument();
} catch (UserQuestionException uqe) {
String title = NbBundle.getMessage(
CSSUtils.class,
"CSSUtils.openQuestion"); // NOI18N
Object value = DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation(
uqe.getLocalizedMessage(),
title,
NotifyDescriptor.YES_NO_OPTION));
if (value != NotifyDescriptor.YES_OPTION) {
return false;
}
uqe.confirmed();
doc = ec.openDocument();
}
if (doc != null) {
boolean offset = (lineNo == -1);
int line = offset ? NbDocument.findLineNumber(doc, columnNo) : lineNo;
int column = offset ? columnNo - NbDocument.findLineOffset(doc, line) : columnNo;
if (line != -1) {
Line l = lc.getLineSet().getCurrent(line);
if (l != null) {
l.show(Line.ShowOpenType.OPEN, Line.ShowVisibilityType.FOCUS, column);
return true;
}
}
}
}
if (oc != null) {
oc.open();
return true;
}
} catch (IOException ioe) {
Logger.getLogger(CSSUtils.class.getName()).log(Level.INFO, null, ioe);
}
return false;
}
/**
* Returns an unspecified "normalized" version of the selector suitable
* for {@code String} comparison with other normalized selectors.
*
* @param selector selector to normalize.
* @return "normalized" version of the selector.
*/
public static String normalizeSelector(String selector) {
// Hack that simplifies the following cycle: adding a dummy
// character that ensures that the last group is ended.
// This character is removed at the end of this method.
selector += 'A';
String whitespaceChars = " \t\n\r\f\""; // NOI18N
String specialChars = ".>+~#:*()[]|,="; // NOI18N
StringBuilder main = new StringBuilder();
StringBuilder group = null;
for (int i=0; i insert single space instead
main.append(' ');
} else {
// group with special chars
main.append(group);
}
group = null;
}
main.append(c);
}
}
// Removing the dummy character added at the beginning of the method
return main.substring(0, main.length()-1).trim();
}
/**
* Returns an unspecified "normalized" version of the media query suitable
* for {@code String} comparison with other normalized media queries.
*
* @param mediaQueryList media query list to normalize.
* @return "normalized" version of the media query.
*/
public static String normalizeMediaQuery(String mediaQueryList) {
mediaQueryList = mediaQueryList.trim().toLowerCase(Locale.ENGLISH);
StringBuilder result = new StringBuilder();
StringTokenizer st = new StringTokenizer(mediaQueryList, ","); // NOI18N
while (st.hasMoreTokens()) {
String mediaQuery = st.nextToken();
int index;
List parts = new ArrayList();
while ((index = mediaQuery.indexOf("and")) != -1) { // NOI18N
String part = mediaQuery.substring(0,index);
mediaQuery = mediaQuery.substring(index+3);
// 'part' is not a selector, but the same normalization
// works well here as well.
part = normalizeSelector(part);
parts.add(part);
}
mediaQuery = normalizeSelector(mediaQuery);
parts.add(mediaQuery);
Collections.sort(parts);
Collections.reverse(parts); // Make sure that media type is before expressions
for (int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy