001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.core.model;
007
008import com.fasterxml.jackson.core.JsonLocation;
009
010import javax.xml.stream.Location;
011
012import edu.umd.cs.findbugs.annotations.NonNull;
013
014/**
015 * A simple implementation of {@link IMetaschemaData} that stores location
016 * information from various parser sources.
017 * <p>
018 * This class can be used both for bound object metadata during parsing and for
019 * validation context in error messages.
020 */
021public final class SimpleResourceLocation implements IMetaschemaData {
022  /** A constant representing an unknown location. */
023  @NonNull
024  public static final IMetaschemaData UNKNOWN = new SimpleResourceLocation(-1, -1, -1L, -1L);
025
026  private final int line;
027  private final int column;
028  private final long charOffset;
029  private final long byteOffset;
030
031  /**
032   * Construct a new resource location with the specified values.
033   *
034   * @param line
035   *          the line number (1-based), or -1 if unknown
036   * @param column
037   *          the column number (1-based), or -1 if unknown
038   * @param charOffset
039   *          the character offset (0-based), or -1 if unknown
040   * @param byteOffset
041   *          the byte offset (0-based), or -1 if unknown
042   */
043  public SimpleResourceLocation(int line, int column, long charOffset, long byteOffset) {
044    this.line = line;
045    this.column = column;
046    this.charOffset = charOffset;
047    this.byteOffset = byteOffset;
048  }
049
050  /**
051   * Create a resource location from an XML stream location.
052   *
053   * @param location
054   *          the XML stream location, may be {@code null}
055   * @return a new resource location, or {@link #UNKNOWN} if the input is null
056   */
057  @NonNull
058  public static IMetaschemaData fromXmlLocation(Location location) {
059    if (location == null) {
060      return UNKNOWN;
061    }
062    return new SimpleResourceLocation(
063        location.getLineNumber(),
064        location.getColumnNumber(),
065        location.getCharacterOffset(),
066        -1L);
067  }
068
069  /**
070   * Create a resource location from a Jackson JSON location.
071   *
072   * @param location
073   *          the JSON location, may be {@code null}
074   * @return a new resource location, or {@link #UNKNOWN} if the input is null
075   */
076  @NonNull
077  public static IMetaschemaData fromJsonLocation(JsonLocation location) {
078    if (location == null) {
079      return UNKNOWN;
080    }
081    return new SimpleResourceLocation(
082        location.getLineNr(),
083        location.getColumnNr(),
084        location.getCharOffset(),
085        location.getByteOffset());
086  }
087
088  /**
089   * Create a resource location with just line and column information.
090   *
091   * @param line
092   *          the line number (1-based), or -1 if unknown
093   * @param column
094   *          the column number (1-based), or -1 if unknown
095   * @return a new resource location
096   */
097  @NonNull
098  public static IMetaschemaData of(int line, int column) {
099    return new SimpleResourceLocation(line, column, -1L, -1L);
100  }
101
102  @Override
103  public int getLine() {
104    return line;
105  }
106
107  @Override
108  public int getColumn() {
109    return column;
110  }
111
112  @Override
113  public long getCharOffset() {
114    return charOffset;
115  }
116
117  @Override
118  public long getByteOffset() {
119    return byteOffset;
120  }
121
122  @Override
123  public String toString() {
124    StringBuilder sb = new StringBuilder();
125    if (line >= 0) {
126      sb.append(line);
127      if (column >= 0) {
128        sb.append(':').append(column);
129      }
130    } else {
131      sb.append("unknown");
132    }
133    return sb.toString();
134  }
135}