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.juneau.console.views;
018
019/**
020 * The categorical pill palette's slot function.
021 *
022 * <p>
023 * A {@code pill} column marked {@link Render#categorical()} colours each distinct value from a fixed palette of
024 * {@link #SIZE} colours.  The browser picks the slot with the hash documented on {@link #slot(String)}; this class is
025 * the same function in Java, for parity tests and for server-side code that wants to know which slot a value gets
026 * (for example to build a legend).  The renderer does not call it at runtime.
027 *
028 * <p>
029 * {@link #SIZE} is part of the colour contract: changing it recolours every categorical value.
030 *
031 * @since 10.0.0
032 */
033public final class PillPalette {
034
035   /** The number of palette slots (theme tokens {@code --jc-pill-cat-1-*} through {@code --jc-pill-cat-10-*}). */
036   public static final int SIZE = 10;
037
038   private static final int FNV_OFFSET = 0x811c9dc5;
039   private static final int FNV_PRIME = 0x01000193;
040
041   /**
042    * Returns the palette slot for a value.
043    *
044    * <p>
045    * FNV-1a, 32-bit, over the UTF-16 code units of the exact string (no case folding, no trimming), then
046    * {@code (hash mod SIZE) + 1} with the hash treated as unsigned.  Matching is case-sensitive: {@code "Pet"} and
047    * {@code "PET"} are different values with independent slots.  A <jk>null</jk> value hashes as the empty string.
048    *
049    * <p>
050    * Reports only the hash slot, never a slot pinned with {@link Render#category(String,int)}.
051    *
052    * @param value The cell value as displayed.  Can be <jk>null</jk>.
053    * @return The slot, from 1 to {@link #SIZE} inclusive.
054    */
055   public static int slot(String value) {
056      var h = FNV_OFFSET;
057      if (value != null)
058         for (var i = 0; i < value.length(); i++)
059            h = (h ^ value.charAt(i)) * FNV_PRIME;
060      return Integer.remainderUnsigned(h, SIZE) + 1;
061   }
062
063   private PillPalette() {}
064}