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    protected StructuredDataId(final String name, final String[] required, final String[] optional) {
062        int index = -1;
063        if (name != null) {
064            if (name.length() > MAX_LENGTH) {
065                throw new IllegalArgumentException(String.format("Length of id %s exceeds maximum of %d characters",
066                        name, MAX_LENGTH));
067            }
068            index = name.indexOf(AT_SIGN);
069        }
070
071        if (index > 0) {
072            this.name = name.substring(0, index);
073            this.enterpriseNumber = Integer.parseInt(name.substring(index + 1));
074        } else {
075            this.name = name;
076            this.enterpriseNumber = RESERVED;
077        }
078        this.required = required;
079        this.optional = optional;
080    }
081
082    /**
083     * A Constructor that helps conformance to RFC 5424.
084     *
085     * @param name The name portion of the id.
086     * @param enterpriseNumber The enterprise number.
087     * @param required The list of keys that are required for this id.
088     * @param optional The list of keys that are optional for this id.
089     */
090    public StructuredDataId(final String name, final int enterpriseNumber, final String[] required,
091            final String[] optional) {
092        if (name == null) {
093            throw new IllegalArgumentException("No structured id name was supplied");
094        }
095        if (name.contains(AT_SIGN)) {
096            throw new IllegalArgumentException("Structured id name cannot contain an " + Strings.quote(AT_SIGN));
097        }
098        if (enterpriseNumber <= 0) {
099            throw new IllegalArgumentException("No enterprise number was supplied");
100        }
101        this.name = name;
102        this.enterpriseNumber = enterpriseNumber;
103        final String id = name + AT_SIGN + enterpriseNumber;
104        if (id.length() > MAX_LENGTH) {
105            throw new IllegalArgumentException("Length of id exceeds maximum of 32 characters: " + id);
106        }
107        this.required = required;
108        this.optional = optional;
109    }
110
111    /**
112     * Creates an id using another id to supply default values.
113     *
114     * @param id The original StructuredDataId.
115     * @return the new StructuredDataId.
116     */
117    public StructuredDataId makeId(final StructuredDataId id) {
118        if (id == null) {
119            return this;
120        }
121        return makeId(id.getName(), id.getEnterpriseNumber());
122    }
123
124    /**
125     * Creates an id based on the current id.
126     *
127     * @param defaultId The default id to use if this StructuredDataId doesn't have a name.
128     * @param anEnterpriseNumber The enterprise number.
129     * @return a StructuredDataId.
130     */
131    public StructuredDataId makeId(final String defaultId, final int anEnterpriseNumber) {
132        String id;
133        String[] req;
134        String[] opt;
135        if (anEnterpriseNumber <= 0) {
136            return this;
137        }
138        if (this.name != null) {
139            id = this.name;
140            req = this.required;
141            opt = this.optional;
142        } else {
143            id = defaultId;
144            req = null;
145            opt = null;
146        }
147
148        return new StructuredDataId(id, anEnterpriseNumber, req, opt);
149    }
150
151    /**
152     * Returns a list of required keys.
153     *
154     * @return a List of required keys or null if none have been provided.
155     */
156    public String[] getRequired() {
157        return required;
158    }
159
160    /**
161     * Returns a list of optional keys.
162     *
163     * @return a List of optional keys or null if none have been provided.
164     */
165    public String[] getOptional() {
166        return optional;
167    }
168
169    /**
170     * Returns the StructuredDataId name.
171     *
172     * @return the StructuredDataId name.
173     */
174    public String getName() {
175        return name;
176    }
177
178    /**
179     * Returns the enterprise number.
180     *
181     * @return the enterprise number.
182     */
183    public int getEnterpriseNumber() {
184        return enterpriseNumber;
185    }
186
187    /**
188     * Indicates if the id is reserved.
189     *
190     * @return true if the id uses the reserved enterprise number, false otherwise.
191     */
192    public boolean isReserved() {
193        return enterpriseNumber <= 0;
194    }
195
196    @Override
197    public String toString() {
198        final StringBuilder sb = new StringBuilder(name.length() + 10);
199        formatTo(sb);
200        return sb.toString();
201    }
202
203    @Override
204    public void formatTo(final StringBuilder buffer) {
205        if (isReserved()) {
206            buffer.append(name);
207        } else {
208            buffer.append(name).append(AT_SIGN).append(enterpriseNumber);
209        }
210    }
211}