001package com.identityworksllc.iiq.common.iterators;
002
003import sailpoint.api.SailPointContext;
004import sailpoint.tools.GeneralException;
005import sailpoint.tools.RFC4180LineParser;
006import sailpoint.tools.Util;
007
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.List;
011
012/**
013 * The generic implementation of a colon-separated column token, e.g., 'col1:blah:stuff'.
014 * The first component of a column token is always the column name, as it would be recognized
015 * by JDBC's ResultSet class. If present, the second component is always a type token.
016 *
017 * At this time, type tokens are understood by {@link ResultSetIterator#deriveTypedValue(SailPointContext, Object, String)}.
018 * See that method's documentation for the accepted type token values.
019 *
020 * If a third (or more) component is present, they will be considered arguments to the type
021 * interpreter and vary by type. For example, a timestamp type may accept a date format.
022 *
023 * Token components are split using Sailpoint's {@link RFC4180LineParser}, meaning that quotes
024 * are respected like a typical CSV.
025 */
026public final class ColumnToken {
027    /**
028     * The base column name
029     */
030    private String baseColumnName;
031
032    /**
033     * Any type parameters passed after the 2nd argument
034     */
035    private final List<String> typeParameters;
036
037    /**
038     * The type token
039     */
040    private String typeToken;
041
042    /**
043     * Creates a new column token object from the input
044     *
045     * @param input The input string of the form described in deriveTypedValue
046     */
047    public ColumnToken(String input) {
048        if (Util.isNullOrEmpty(input)) {
049            throw new IllegalArgumentException("Column token string cannot be null or empty");
050        }
051
052        this.typeParameters = new ArrayList<>();
053
054        try {
055            RFC4180LineParser parser = new RFC4180LineParser(ColumnConfig.COLUMN_TYPE_SEPARATOR);
056            List<String> pieces = parser.parseLine(input);
057
058            if (pieces.size() == 1) {
059                this.baseColumnName = pieces.get(0);
060                this.typeToken = null;
061            } else if (pieces.size() >= 2) {
062                this.baseColumnName = pieces.get(0);
063                this.typeToken = pieces.get(1);
064                if (pieces.size() >= 3) {
065                    for (int p = 2; p < pieces.size(); p++) {
066                        String piece = pieces.get(p);
067                        if (piece == null) {
068                            piece = "";
069                        }
070                        this.typeParameters.add(piece);
071                    }
072                }
073            }
074        } catch (GeneralException e) {
075            throw new IllegalArgumentException("Unparseable input: " + input, e);
076        }
077    }
078
079    /**
080     * Returns the base column name (the start of the token)
081     *
082     * @return The base column name
083     */
084    public String getBaseColumnName() {
085        return baseColumnName;
086    }
087
088    /**
089     * Returns the given type parameter, or null if the index given is out of bounds
090     *
091     * @param index The type parameter index
092     * @return The value requested, or null if not defined
093     */
094    public String getTypeParameter(int index) {
095        if (index < this.typeParameters.size() && index >= 0) {
096            return this.typeParameters.get(index);
097        } else {
098            return null;
099        }
100    }
101
102    /**
103     * Gets the list of type parameters, which is always non-null, but may be empty
104     *
105     * @return The list of type parameters
106     */
107    public List<String> getTypeParameters() {
108        return Collections.unmodifiableList(typeParameters);
109    }
110
111    /**
112     * Gets the type token part of the string, e.g., 'xml' or 'timestamp'
113     *
114     * @return The type token part of the input string
115     */
116    public String getTypeToken() {
117        return typeToken;
118    }
119}