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.logging.log4j.message;
018
019import java.io.Serializable;
020
021import org.apache.logging.log4j.util.StringBuilderFormattable;
022import org.apache.logging.log4j.util.Strings;
023
024/**
025 * The StructuredData identifier.
026 */
027public class StructuredDataId implements Serializable, StringBuilderFormattable {
028
029    /**
030     * RFC 5424 Time Quality.
031     */
032    public static final StructuredDataId TIME_QUALITY = new StructuredDataId("timeQuality", null, new String[] {
033            "tzKnown", "isSynced", "syncAccuracy"});
034
035    /**
036     * RFC 5424 Origin.
037     */
038    public static final StructuredDataId ORIGIN = new StructuredDataId("origin", null, new String[] {"ip",
039            "enterpriseId", "software", "swVersion"});
040
041    /**
042     * RFC 5424 Meta.
043     */
044    public static final StructuredDataId META = new StructuredDataId("meta", null, new String[] {"sequenceId",
045            "sysUpTime", "language"});
046
047    /**
048     * Reserved enterprise number.
049     */
050    public static final int RESERVED = -1;
051
052    private static final long serialVersionUID = 9031746276396249990L;
053    private static final int MAX_LENGTH = 32;
054    private static final String AT_SIGN = "@";
055
056    private final String name;
057    private final int enterpriseNumber;
058    private final String[] required;
059    private final String[] optional;
060
061    /**
062     * Creates a StructuredDataId based on the name.
063     * @param name The Structured Data Element name (maximum length is 32)
064     * @since 2.9
065     */
066    public StructuredDataId(final String name) {
067        this(name, null, null, MAX_LENGTH);
068    }
069
070    /**
071     * Creates a StructuredDataId based on the name.
072     * @param name The Structured Data Element name.
073     * @param maxLength The maximum length of the name.
074     * @since 2.9
075     */
076    public StructuredDataId(final String name, final int maxLength) {
077        this(name, null, null, maxLength);
078    }
079
080    /**
081     *
082     * @param name
083     * @param required
084     * @param optional
085     */
086    public StructuredDataId(final String name, final String[] required, final String[] optional) {
087        this(name, required, optional, MAX_LENGTH);
088    }
089
090    /**
091     * A Constructor that helps conformance to RFC 5424.
092     *
093     * @param name The name portion of the id.
094     * @param required The list of keys that are required for this id.
095     * @param optional The list of keys that are optional for this id.
096     * @since 2.9
097     */
098    public StructuredDataId(final String name, final String[] required, final String[] optional, int maxLength) {
099        int index = -1;
100        if (name != null) {
101            if (maxLength <= 0) {
102                maxLength = MAX_LENGTH;
103            }
104            if (name.length() > maxLength) {
105                throw new IllegalArgumentException(String.format("Length of id %s exceeds maximum of %d characters",
106                        name, maxLength));
107            }
108            index = name.indexOf(AT_SIGN);
109        }
110
111        if (index > 0) {
112            this.name = name.substring(0, index);
113            this.enterpriseNumber = Integer.parseInt(name.substring(index + 1));
114        } else {
115            this.name = name;
116            this.enterpriseNumber = RESERVED;
117        }
118        this.required = required;
119        this.optional = optional;
120    }
121
122    /**
123     * A Constructor that helps conformance to RFC 5424.
124     *
125     * @param name The name portion of the id.
126     * @param enterpriseNumber The enterprise number.
127     * @param required The list of keys that are required for this id.
128     * @param optional The list of keys that are optional for this id.
129     */
130    public StructuredDataId(final String name, final int enterpriseNumber, final String[] required,
131                            final String[] optional) {
132        this(name, enterpriseNumber, required, optional, MAX_LENGTH);
133    }
134
135    /**
136     * A Constructor that helps conformance to RFC 5424.
137     *
138     * @param name The name portion of the id.
139     * @param enterpriseNumber The enterprise number.
140     * @param required The list of keys that are required for this id.
141     * @param optional The list of keys that are optional for this id.
142     * @param maxLength The maximum length of the StructuredData Id key.
143     * @since 2.9
144     */
145    public StructuredDataId(final String name, final int enterpriseNumber, final String[] required,
146            final String[] optional, final int maxLength) {
147        if (name == null) {
148            throw new IllegalArgumentException("No structured id name was supplied");
149        }
150        if (name.contains(AT_SIGN)) {
151            throw new IllegalArgumentException("Structured id name cannot contain an " + Strings.quote(AT_SIGN));
152        }
153        if (enterpriseNumber <= 0) {
154            throw new IllegalArgumentException("No enterprise number was supplied");
155        }
156        this.name = name;
157        this.enterpriseNumber = enterpriseNumber;
158        final String id = name + AT_SIGN + enterpriseNumber;
159        if (maxLength > 0 && id.length() > maxLength) {
160            throw new IllegalArgumentException("Length of id exceeds maximum of " + maxLength + " characters: " + id);
161        }
162        this.required = required;
163        this.optional = optional;
164    }
165
166    /**
167     * Creates an id using another id to supply default values.
168     *
169     * @param id The original StructuredDataId.
170     * @return the new StructuredDataId.
171     */
172    public StructuredDataId makeId(final StructuredDataId id) {
173        if (id == null) {
174            return this;
175        }
176        return makeId(id.getName(), id.getEnterpriseNumber());
177    }
178
179    /**
180     * Creates an id based on the current id.
181     *
182     * @param defaultId The default id to use if this StructuredDataId doesn't have a name.
183     * @param anEnterpriseNumber The enterprise number.
184     * @return a StructuredDataId.
185     */
186    public StructuredDataId makeId(final String defaultId, final int anEnterpriseNumber) {
187        String id;
188        String[] req;
189        String[] opt;
190        if (anEnterpriseNumber <= 0) {
191            return this;
192        }
193        if (this.name != null) {
194            id = this.name;
195            req = this.required;
196            opt = this.optional;
197        } else {
198            id = defaultId;
199            req = null;
200            opt = null;
201        }
202
203        return new StructuredDataId(id, anEnterpriseNumber, req, opt);
204    }
205
206    /**
207     * Returns a list of required keys.
208     *
209     * @return a List of required keys or null if none have been provided.
210     */
211    public String[] getRequired() {
212        return required;
213    }
214
215    /**
216     * Returns a list of optional keys.
217     *
218     * @return a List of optional keys or null if none have been provided.
219     */
220    public String[] getOptional() {
221        return optional;
222    }
223
224    /**
225     * Returns the StructuredDataId name.
226     *
227     * @return the StructuredDataId name.
228     */
229    public String getName() {
230        return name;
231    }
232
233    /**
234     * Returns the enterprise number.
235     *
236     * @return the enterprise number.
237     */
238    public int getEnterpriseNumber() {
239        return enterpriseNumber;
240    }
241
242    /**
243     * Indicates if the id is reserved.
244     *
245     * @return true if the id uses the reserved enterprise number, false otherwise.
246     */
247    public boolean isReserved() {
248        return enterpriseNumber <= 0;
249    }
250
251    @Override
252    public String toString() {
253        final StringBuilder sb = new StringBuilder(name.length() + 10);
254        formatTo(sb);
255        return sb.toString();
256    }
257
258    @Override
259    public void formatTo(final StringBuilder buffer) {
260        if (isReserved()) {
261            buffer.append(name);
262        } else {
263            buffer.append(name).append(AT_SIGN).append(enterpriseNumber);
264        }
265    }
266}