All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.transit.model.basic.SubMode Maven / Gradle / Ivy

package org.opentripplanner.transit.model.basic;

import java.io.Serializable;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class wraps a string and creates an index for it, so we can use a BitSet for matching a
 * trip netex SubMode. SubModes are used in trip filtering for every request.
 * 

* Naming; This class is named SubMode, not NetexSubMode because we want to migrate gtfsRouteType * into the same concept. *

* This class is thread-safe, and the performance overhead should affect the graph built time, * but not routing. */ public record SubMode(String name, int index) implements Serializable { private static final int NON_EXISTING_SUB_MODE_INDEX = 1_000_000; private static final Logger LOG = LoggerFactory.getLogger(SubMode.class); /** * Note! This caches all sub-modes used in the static scheduled transit data. When * serializing the static fields need to be explicitly serialized, it is not enough * to serialize the instances. */ private static final Map ALL = new ConcurrentHashMap<>(); private static final AtomicInteger COUNTER = new AtomicInteger(0); public static final SubMode UNKNOWN = getOrBuildAndCacheForever("unknown"); /** * This method is safe to use in a request scope. Usually you want to fetch an instant to * pass in as a request parameter. This method will fetch an existing object - not creating * a duplicate object, if at least one Route, Trip or Stop has this submode. If it does not * exist in the model a new is created, but not put on the "deduplication" map. */ public static SubMode of(String name) { if (name == null) { return UNKNOWN; } var subMode = ALL.get(name); return subMode != null ? subMode : new SubMode(name, NON_EXISTING_SUB_MODE_INDEX); } /** * Make sure to use this during graph build or when creating new Trips in realTime * updates. Do NOT use this in an OTP routing request - that will lead to memory leaks. *

* The builders in the transit model take care of calling this method, so there is no * reason to call this method outside the transit model package. */ public static SubMode getOrBuildAndCacheForever(String name) { if (name == null) { return UNKNOWN; } if (ALL.size() == 1000) { LOG.error("There are 1000 subModes in use, there might be a memory leak."); } return ALL.computeIfAbsent(name, it -> new SubMode(it, COUNTER.getAndIncrement())); } /** * Return all SubModes besed on the BitSet of SubMode indexes. */ public static Set getByIndex(BitSet subModes) { var set = new HashSet(); for (SubMode value : ALL.values()) { if (subModes.get(value.index())) { set.add(value); } } return set; } public static List listAllCachedSubModes() { return List.copyOf(ALL.values()); } public static void deserializeSubModeCache(Collection subModes) { int maxIndex = 0; for (SubMode it : subModes) { ALL.put(it.name(), it); maxIndex = Math.max(maxIndex, it.index); } COUNTER.set(maxIndex + 1); } /** * Only name is used in the equals, not index, so we need to override the * default record implementation of equals. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } var other = (SubMode) o; return name.equals(other.name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return name(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy