001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.beans.factory.parsing;
018
019import java.util.ArrayDeque;
020
021import org.springframework.lang.Nullable;
022
023/**
024 * Simple {@link ArrayDeque}-based structure for tracking the logical position during
025 * a parsing process. {@link Entry entries} are added to the ArrayDeque at each point
026 * during the parse phase in a reader-specific manner.
027 *
028 * <p>Calling {@link #toString()} will render a tree-style view of the current logical
029 * position in the parse phase. This representation is intended for use in error messages.
030 *
031 * @author Rob Harrop
032 * @author Juergen Hoeller
033 * @since 2.0
034 */
035public final class ParseState {
036
037        /**
038         * Internal {@link ArrayDeque} storage.
039         */
040        private final ArrayDeque<Entry> state;
041
042
043        /**
044         * Create a new {@code ParseState} with an empty {@link ArrayDeque}.
045         */
046        public ParseState() {
047                this.state = new ArrayDeque<>();
048        }
049
050        /**
051         * Create a new {@code ParseState} whose {@link ArrayDeque} is a clone
052         * of the state in the passed-in {@code ParseState}.
053         */
054        private ParseState(ParseState other) {
055                this.state = other.state.clone();
056        }
057
058
059        /**
060         * Add a new {@link Entry} to the {@link ArrayDeque}.
061         */
062        public void push(Entry entry) {
063                this.state.push(entry);
064        }
065
066        /**
067         * Remove an {@link Entry} from the {@link ArrayDeque}.
068         */
069        public void pop() {
070                this.state.pop();
071        }
072
073        /**
074         * Return the {@link Entry} currently at the top of the {@link ArrayDeque} or
075         * {@code null} if the {@link ArrayDeque} is empty.
076         */
077        @Nullable
078        public Entry peek() {
079                return this.state.peek();
080        }
081
082        /**
083         * Create a new instance of {@link ParseState} which is an independent snapshot
084         * of this instance.
085         */
086        public ParseState snapshot() {
087                return new ParseState(this);
088        }
089
090
091        /**
092         * Returns a tree-style representation of the current {@code ParseState}.
093         */
094        @Override
095        public String toString() {
096                StringBuilder sb = new StringBuilder(64);
097                int i = 0;
098                for (ParseState.Entry entry : this.state) {
099                        if (i > 0) {
100                                sb.append('\n');
101                                for (int j = 0; j < i; j++) {
102                                        sb.append('\t');
103                                }
104                                sb.append("-> ");
105                        }
106                        sb.append(entry);
107                        i++;
108                }
109                return sb.toString();
110        }
111
112
113        /**
114         * Marker interface for entries into the {@link ParseState}.
115         */
116        public interface Entry {
117        }
118
119}