Home > Articles > Programming > Java

Like this article? We recommend Java CSS Fundamentals

Java CSS Fundamentals

Before you can use Java CSS effectively, you need to be acquainted with various fundamentals. In this section, we'll review some CSS terminology in the context of Figure 1. Then you'll learn how to work with a pair of API classes to perform styling. After discovering the usefulness of global style sheets, we'll take an in-depth tour of Java CSS support for selectors and declarations.

CSS Terminology by Example

Java CSS follows the CSS convention for specifying styling information. According to this convention, a style sheet is a collection of rules, a rule is a selector and its associated brace-delimited list of declarations, a selector is a string that identifies the user interface component(s) to which corresponding declarations apply, and a declaration is a component property and its associated value.

Figure 1 reveals four rules. In the first rule, #title is the selector and font: Helvetica-BOLD-24 is the declaration. Java CSS interprets this rule as follows: Set the font property to a 24-point bold Helvetica font for all Swing components whose ID is set to title (via java.awt.Component's setName() method). Only the Tip Calculator label has its ID set to title.

In the second rule, .bold is the selector and font-weight: bold is the declaration. Java CSS sets the font-weight property to bold for all Swing components whose style class is set to bold (via javax.swing.JComponent's putClientProperty() method, whose key parameter is assigned "styleClass"). Only the Total: label belongs to the bold style class.

The third rule specifies JSlider#tip as the selector and specifies six declarations. For the JSlider with the tip ID, Java CSS sets paintTicks to true (show tick marks), majorTickSpacing to 10 and minorTickSpacing to 5 (specify major and minor tick mark spacing), paintLabels to true (show labels), background to null (no background color), and opaque to true (hide background pixels).

Finally, the fourth rule specifies JSlider#tip:{value <= 10} as the selector and background: red !over 0.3s as the declaration. For the JSlider identified as tip, Java CSS animates its background property from null to red over a 0.3-second duration whenever the slider's value property is less than or equal to 10, and animates the background property from red to null whenever value exceeds 10.

CSSParser and StyleSheet

In Java CSS, more than 50 interfaces, classes, and enumerations are organized into packages com.sun.stylesheet, com.sun.stylesheet.css, com.sun.stylesheet.styleable, and com.sun.stylesheet.types. Classes com.sun.stylesheet.css.CSSParser and com.sun.stylesheet.Stylesheet serve as the API's entry point:

  • CSSParser provides six methods for parsing and re-parsing a style sheet. These methods either return a Stylesheet object (parsing) or take an existing Stylesheet object as an argument (re-parsing). Each method throws a com.sun.stylesheet.css.ParseException when parsing errors are encountered. Two of the methods that obtain a style sheet from a URL throw an IOException if a network problem occurs, and one of the URL-parsing methods also throws an UnsupportedEncodingException if its encoding argument isn't supported.
  • Stylesheet describes a style sheet that can apply properties to a tree of objects. Basically, a Stylesheet object is a collection of com.sun.stylesheet.Rule objects, with each Rule object describing a specific rule in terms of one or more com.sun.stylesheet.Selectors and com.sun.stylesheet.Declarations. (I'll talk about multiple selectors for a single rule later.) For each Rule, the tree is scanned and each object is checked against the Selector(s) to see whether the Rule applies. If so, the Rule applies Declaration property values to the object's properties.

After parsing a style sheet and returning a Stylesheet object, an application typically applies the style sheet's rules to its tree of component objects by invoking Stylesheet's public void applyTo(Object root) method, where root is often a reference to the application's frame window. Subsequent changes to a style sheet can be reapplied to this tree via Stylesheet's public void reapply() method.

Listing 1 demonstrates using CSSParser's public static Stylesheet parse(String stylesheetText) method to parse a String-based style sheet, and demonstrates using applyTo() to apply the parsed style sheet's properties to a tree of five buttons rooted in their frame window container.

Listing 1—FiveButtons.java.

// FiveButtons.java

import java.awt.EventQueue;
import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JFrame;

import com.sun.stylesheet.Stylesheet;

import com.sun.stylesheet.css.CSSParser;

public class FiveButtons extends JFrame
{
   public FiveButtons ()
   {
      super ("Five Buttons");
      setDefaultCloseOperation (EXIT_ON_CLOSE);

      getContentPane ().setLayout (new FlowLayout ());

      JButton btn1 = new JButton ("First");
      getContentPane ().add (btn1);

      JButton btn2 = new JButton ("Second");
      btn2.setName ("second");
      getContentPane ().add (btn2);

      JButton btn3 = new JButton ("Third");
      btn3.putClientProperty ("styleClass", "italic");
      getContentPane ().add (btn3);

      JButton btn4 = new JButton ("Fourth");
      btn4.putClientProperty ("styleClass", "italic");
      getContentPane ().add (btn4);

      JButton btn5 = new JButton ("Fifth");
      getContentPane ().add (btn5);

      pack ();
      setVisible (true);
   }

   public static void main (String [] args)
   {
      Runnable r;
      r = new Runnable ()
          {
              public void run ()
              {
                 String styling =
                   "JButton { foreground: red }"+
                   "JButton#second { font-size: 20pt; foreground: blue }"+
                   "JButton.italic { font-style: italic; foreground: #008000 }";
                 Stylesheet stylesheet = CSSParser.parse (styling);
                 FiveButtons fb = new FiveButtons ();
                 stylesheet.applyTo (fb);
              }
          };
      EventQueue.invokeLater (r);
   }
}

These buttons are styled via a String-based style sheet that assigns red text to buttons that are unnamed and that belong to no class. Further, the button with an ID matching second (only one component should match an ID; otherwise use a style class) presents blue 20-point text, and all buttons belonging to the italic class present dark green italic text. Figure 2 shows the resulting styled user interface.

Figure 2 Styling a handful of buttons.

Global Style Sheets

The previous approach to styling a user interface doesn't work where option panes and other dialog boxes are involved. To style dialog boxes, you need to take advantage of the Java CSS global style sheet feature, which lets you install a style sheet whose settings apply to all newly opened windows, without affecting existing windows.

We invoke Stylesheet's public static void setGlobalStylesheet(Stylesheet stylesheet) method to install stylesheet as the global style sheet. If a global style sheet is already present, it's removed, and any of its currently styled windows are updated to match the new style sheet. The companion public static Stylesheet getGlobalStylesheet() method returns the current global style sheet.

To demonstrate the global style sheet's usefulness, I've created an application that presents a user interface consisting of a text field and a button. If the text field doesn't contain an integer when the button is clicked, a NumberFormatException is thrown. Its handler installs a global style sheet prior to presenting an option pane, to style the pane's button. Listing 2 presents the application's source code.

Listing 2—GSDemo.java.

// GSDemo.java

import java.awt.EventQueue;
import java.awt.FlowLayout;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

import com.sun.stylesheet.Stylesheet;

import com.sun.stylesheet.css.CSSParser;

public class GSDemo extends JFrame
{
   static Stylesheet globalStylesheet;

   public GSDemo ()
   {
      super ("Global Style Sheet Demo");
      setDefaultCloseOperation (EXIT_ON_CLOSE);

      getContentPane ().setLayout (new FlowLayout ());

      getContentPane ().add (new JLabel ("Age"));

      final JTextField txtAge = new JTextField (20);
      getContentPane ().add (txtAge);

      JButton btnValidate = new JButton ("Validate");

      ActionListener al;
      al = new ActionListener ()
           {
               public void actionPerformed (ActionEvent ae)
               {
                  try
                  {
                      Integer.parseInt (txtAge.getText ());
                  }
                  catch (NumberFormatException nfe)
                  {
                      Stylesheet.setGlobalStylesheet (globalStylesheet);
                      JOptionPane.showMessageDialog (GSDemo.this,
                                                     "integer expected");
                  }
               }
           };
      btnValidate.addActionListener (al);
      getContentPane ().add (btnValidate);
   }

   public static void main (String [] args)
   {
      Runnable r;
      r = new Runnable ()
          {
              public void run ()
              {
                 String globalStyling =
                   "JButton { font-size: 16pt; foreground: red; "+
                   "text-decoration: underline }";
                 globalStylesheet = CSSParser.parse (globalStyling);

                 String styling =
                   "JButton { font-size: 16pt; foreground: blue; "+
                   "text-decoration: underline }";
                 Stylesheet stylesheet = CSSParser.parse (styling);
                 GSDemo gsdemo = new GSDemo ();
                 stylesheet.applyTo (gsdemo);
                 gsdemo.pack ();
                 gsdemo.setVisible (true);
              }
          };
      EventQueue.invokeLater (r);
   }
}

After creating global and non-global style sheets, the application invokes GSDemo() to create the user interface and applies the non-global style sheet to this interface. In this example, it's necessary to apply the style sheet prior to packing and displaying the user interface, to ensure that Swing takes the styling information into account when sizing the frame window and laying out its components.

Prior to invoking JOptionPane.showMessageDialog() in response to the thrown exception, the application executes Stylesheet.setGlobalStylesheet (globalStylesheet); to install the global style sheet. As Figure 3 shows, this method call allows the option pane's button text to be colored red, whereas the frame window's button text is colored blue.

Figure 3 You must use a global style sheet to style an option pane.

Perhaps you're wondering why I placed Stylesheet.setGlobalStylesheet (globalStylesheet); in the exception handler, instead of placing it after gsdemo.setVisible (true); in main()'s run() method. If I had done that, you'd notice the frame window's button text colored red instead of blue. This seems to contradict what I said earlier about the global style sheet not affecting existing windows.

Behind the scenes, however, setGlobalStylesheet() registers a window listener (if not registered) to listen for window-opening events. When this event occurs, the listener applies global style sheet styles to the window. Because the window-opening event will not occur until run() exits, placing setGlobalStylesheet() in run() allows the registered window listener to intercept this event, and color the button text red.

More About Selectors

Each style sheet rule begins with a selector, a string-based expression that identifies the Swing (or Abstract Window Toolkit) component(s) to which the corresponding declarations apply. Java CSS recognizes four basic kinds of selectors, which can be combined into more complex selectors:

  • Java class: The name of a Java class without package information. All instances of classes matching this name and all instances of subclasses are selected. When specified, this name must appear at the beginning of the selector. Alternatively, an asterisk may appear in place of the Java class name. If present, this wildcard matches all Java classes.
  • ID: An object's identifier. For objects whose classes descend from java.awt.Component, the ID is specified via Component's public void setName(String name) method and returned via its public String getName() method. The ID follows the Java class name (if present) in the selector and is prefixed with a hash character (#).
  • Style class: A Swing component's style class name (bold, for example). For objects whose classes descend from javax.swing.JComponent, the style class is specified via JComponent's public final void putClientProperty(Object key, Object value) method and returned via its public final Object getClientProperty(Object key) method. In either case, key is set to "styleClass" (although it's preferable to specify Stylesheet.STYLE_CLASS_KEY). All components with matching style classes are selected. The style class follows any Java class name and ID in the selector, and is prefixed with a period (.).
  • Pseudoclass: The category to which a component belongs at certain times, based on the component's current state. A pseudoclass appears at the end of a selector and is prefixed with a colon (:) character. Pseudoclasses are inherently dynamic, and their values may be applied to and removed from the component many times during the course of execution.
  • The following pseudoclasses are available:

  • mouseover—applies when the mouse is over the component.
  • mouseout—applies when the mouse is outside of the component.
  • mousedown—applies when the mouse is pressed on the component.
  • mouseup—applies when the mouse is not pressed on the component.
  • focused—applies when the component has focus.
  • unfocused—applies when the component doesn't have focus.
  • armed—applies when an AbstractButton is armed.
  • unarmed—applies when an AbstractButton isn't armed.
  • active—applies when a Window is active.
  • inactive—applies when a Window is inactive.
  • selected—applies when the component's isSelected() method returns true. This pseudoclass is shorthand for the ${selected} programmatic pseudoclass.
  • unselected—applies when the component's isSelected() method returns false. This pseudoclass is shorthand for the ${!selected} programmatic pseudoclass.
  • programmatic—any Boolean-valued Beans Binding expression can be used as a pseudoclass. For example, JTextField:{text == ""} { background: red } tells Java CSS to color a text field's background red when it contains no text (as a reminder to the user that a value must be entered into the text field). Behind the scenes, text == "" is treated as getText ().equals ("").

For example, JButton and * are Java class selectors, #title is an ID selector, .bold is a style class selector, and JTextField.focused is a combined Java class and pseudoclass selector. In addition to supporting these and other simple selectors, Java CSS lets you combine simple selectors into compound selectors, as follows:

  • E F—any object matched by F that is a descendent of an object matched by E. For example, Window:mouseover JButton { foreground: #008000 } changes the foreground color of all JButtons to dark green whenever the mouse moves over the window (but not over a button or other child component) containing these buttons.
  • E > F—any object matched by F that is the child of an object matched by E. For example, JPanel#panel > JButton { foreground: red } changes the foreground color to red for all JButtons that are added to the JPanel named panel (but not added to containers that are added to this panel).

Finally, multiple selectors can be specified for a single rule, provided that each selector is separated from its predecessor via a comma. Java CSS will apply the rule to selector-specific components if it's able to match any of the selectors to existing components. For example, JButton, JLabel { foreground: blue } specifies blue as the foreground color of all JButtons and JLabels.

More About Declarations

A style sheet's rule ends with a brace-delimited list of declarations arranged into property name/value pairs. Each property name must be separated from its value via a colon (:) character. Furthermore, each declaration must be separated from its successor via a semicolon (;). You also have the option to place a semicolon after the final declaration and before the closing brace character.

Java CSS lets you tag a declaration value with the !over modifier to provide a smooth animated transition between property values. This modifier must be followed by a duration value specified in milliseconds (500ms), seconds (10s), or minutes (1.5m). In Figure 1, for example, red !over 0.3s causes the background of the slider identified as tip to transition from its current color to red over a 0.3-second interval.

By default, this animation is nonlinear, with slight acceleration at the beginning and deceleration at the end. However, you can append a keyword from the following list to !over and its duration to change the form of animation:

  • default: Use the default curve, with slight acceleration at the beginning and deceleration at the end.
  • linear: No acceleration or deceleration.
  • ease-in: Animation starts slow and accelerates.
  • ease-out: Animation starts fast and decelerates.
  • ease-in-out: Stronger acceleration and deceleration than default.

For example, appending linear (as in !over 0.8s linear) removes the acceleration and deceleration from the animation.

Java CSS also lets you tag a declaration value with the !important modifier to increase a declaration's priority, so that the declaration overrides similar declarations that are not tagged with this modifier. For example, suppose a style sheet contains the following two rules, which pertain to the user interface's text fields:

JTextField:{text == ""} { background: red !important }
JTextField#foo:{text == ""} {background: pink }

Normally, the JTextField with an ID matching foo would have its background colored pink whenever this text field contained no text (JTextField#foo is more specific than JTextField). However, !important forces foo's background to be colored red.

Many of the properties previously presented are associated with setter methods that Java CSS invokes to set property values. For example, JSlider's paintTicks property is associated with a setPaintTicks() setter method. However, Java CSS also generates a few synthetic properties (for convenience) that don't have setter methods:

  • font-family: Font family name (such as Arial).
  • font-size: Expressed using standard CSS units (14pt or 0.5in, for example). Percentage sizes such as 150% are also supported.
  • font-style: plain or italic.
  • font-weight: normal or bold.
  • text-decoration: none, underline, or line-through.

Java CSS automatically generates the first four synthetic properties for any object having a font property; that is, setting the font property automatically sets the font-family, font-size, font-style, and font-weight synthetic properties. Java CSS automatically generates the final synthetic property for JLabels and AbstractButtons.

Finally, each property has its own type, which implies a set of values that can be legally associated with the property's name. Java CSS supports the property types (and their value sets) listed below:

  • boolean or Boolean: true or false.
  • Border: Calls to BorderFactory methods, such as BorderFactory.createEtchedBorder(), or null.
  • byte or Byte: Any string accepted by Byte.valueOf().
  • char or Character: Any string of length 1.
  • Color: # followed by six hexadecimal digits, or one of the following, where r, g, b, and a are placeholders for integer values ranging from 0–255:
    • new java.awt.Color (r, g, b)
    • new java.awt.Color (r, g, b, a)
    • new Color (r, g, b)
    • new Color (r, g, b, a)
    • java.awt.Color.BLUE (or other field constant)
    • Color.blue (or other field constant)
    • red (or other field constant)
    • null
  • Dimension: Two comma-separated integer values; a call to Dimension's public Dimension(int width, int height) constructor, as in new Dimension(200, 100); or null.
  • double or Double: Any string accepted by Double.valueOf().
  • Enum: Any enumerated constant name.
  • float or Float: Any string accepted by Float.valueOf().
  • Font: Any string accepted by Font.decode(), as in Arial-BOLD-18, or null.
  • Insets: Four comma-separated integer values; a call to Insets' public Insets(int top, int left, int bottom, int right) constructor, as in new Insets(2, 2, 2, 2); or null.
  • int or Integer: Any string accepted by Integer.valueOf().
  • Keystroke: Any string accepted by Keystroke.getKeyStroke(), or null.
  • long or Long: Any string accepted by Long.valueOf().
  • short or Short: Any string accepted by Short.valueOf().
  • String: Any string surrounded by single or double quotes (the string may contain \", \', \\, \n, \r, \t, or \b escape sequences), or null.
  • com.sun.stylesheet.types.Size: Any string representing a size expressed in percentages (10%), inches (3.5in), centimeters (25cm), millimeters (60.8mm), points (72pt), pixels (600px), picas (10.2pc), ems (1.0em), or exes (22ex).
  • com.sun.stylesheet.types.Time: Any string representing a time expressed in milliseconds (500ms), seconds (20.5s), or minutes (50.3m).

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020