/*
* MapParser.java February 2005
*
* Copyright (C) 2005, Niall Gallagher <niallg@users.sf.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.simpleframework.util.parse;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The <code>MapParser</code> object represents a parser for name value pairs.
* Any parser extending this will typically be parsing name=value tokens or the
* like, and inserting these pairs into the internal map. This type of parser is
* useful as it exposes all pairs extracted using the <code>java.util.Map</code>
* interface and as such can be used with the Java collections framework. The
* internal map used by this is a <code>Hashtable</code>, however subclasses are
* free to assign a different type to the map used.
*
* @author Niall Gallagher
*/
public abstract class MapParser<T> extends Parser implements Map<T, T> {
/**
* Represents all values inserted to the map as a list of values.
*/
protected Map<T, List<T>> all;
/**
* Represents the last value inserted into this map instance.
*/
protected Map<T, T> map;
/**
* Constructor for the <code>MapParser</code> object. This is used to create
* a new parser that makes use of a thread safe map implementation. The
* <code>HashMap</code> is used so that the resulting parser can be accessed
* in a concurrent environment with the fear of data corruption.
*/
protected MapParser() {
this.all = new HashMap<T, List<T>>();
this.map = new HashMap<T, T>();
}
/**
* This is used to determine whether a token representing the name of a pair
* has been inserted into the internal map. The object passed into this
* method should be a string, as all tokens stored within the map will be
* stored as strings.
*
* @param name
* this is the name of a pair within the map
*
* @return this returns true if the pair of that name exists
*/
@Override
public boolean containsKey(Object name) {
return this.map.containsKey(name);
}
/**
* This method is used to determine whether any pair that has been inserted
* into the internal map had the presented value. If one or more pairs
* within the collected tokens contains the value provided then this method
* will return true.
*
* @param value
* this is the value that is to be searched for
*
* @return this returns true if any value is equal to this
*/
@Override
public boolean containsValue(Object value) {
return this.map.containsValue(value);
}
/**
* This method is used to acquire the name and value pairs that have
* currently been collected by this parser. This is used to determine which
* tokens have been extracted from the source. It is useful when the tokens
* have to be gathered.
*
* @return this set of token pairs that have been extracted
*/
@Override
public Set<Map.Entry<T, T>> entrySet() {
return this.map.entrySet();
}
/**
* The <code>get</code> method is used to acquire the value for a named
* pair. So if a pair of name=value has been parsed and inserted into the
* collection of tokens this will return the value given the name. The value
* returned will be a string.
*
* @param name
* this is a string used to search for the value
*
* @return this is the value, as a string, that has been found
*/
@Override
public T get(Object name) {
return this.map.get(name);
}
/**
* This method is used to acquire a <code>List</code> for all of the values
* that have been put in to the map. The list allows all values associated
* with the specified key. This enables a parser to collect a number of
* associated tokens.
*
* @param key
* this is the key used to search for the value
*
* @return this is the list of values associated with the key
*/
public List<T> getAll(Object key) {
return this.all.get(key);
}
/**
* This method is used to determine whether the parser has any tokens
* available. If the <code>size</code> is zero then the parser is empty and
* this returns true. The is acts as a proxy the the <code>isEmpty</code> of
* the internal map.
*
* @return this is true if there are no available tokens
*/
@Override
public boolean isEmpty() {
return this.map.isEmpty();
}
/**
* This is used to acquire the names for all the tokens that have currently
* been collected by this parser. This is used to determine which tokens
* have been extracted from the source. It is useful when the tokens have to
* be gathered.
*
* @return the set of name tokens that have been extracted
*/
@Override
public Set<T> keySet() {
return this.map.keySet();
}
/**
* The <code>put</code> method is used to insert the name and value provided
* into the collection of tokens. Although it is up to the parser to decide
* what values will be inserted it is generally the case that the inserted
* tokens will be text.
*
* @param name
* this is the name token from a name=value pair
* @param value
* this is the value token from a name=value pair
*
* @return this returns the previous value if there was any
*/
@Override
public T put(T name, T value) {
List<T> list = this.all.get(name);
T first = this.map.get(name);
if (list == null) {
list = new ArrayList<T>();
this.all.put(name, list);
}
list.add(value);
if (first == null) return this.map.put(name, value);
return null;
}
/**
* This method is used to insert a collection of tokens into the parsers
* map. This is used when another source of tokens is required to populate
* the connection currently maintained within this parsers internal map. Any
* tokens that currently exist with similar names will be overwritten by
* this.
*
* @param data
* this is the collection of tokens to be added
*/
@Override
public void putAll(Map<? extends T, ? extends T> data) {
Set<? extends T> keySet = data.keySet();
for (T key : keySet) {
T value = data.get(key);
if (value != null) {
this.put(key, value);
}
}
}
/**
* The <code>remove</code> method is used to remove the named token pair
* from the collection of tokens. This acts like a take, in that it will get
* the token value and remove if from the collection of tokens the parser
* has stored.
*
* @param name
* this is a string used to search for the value
*
* @return this is the value, as a string, that is removed
*/
@Override
public T remove(Object name) {
return this.map.remove(name);
}
/**
* This obviously enough provides the number of tokens that have been
* inserted into the internal map. This acts as a proxy method for the
* internal map <code>size</code>.
*
* @return this returns the number of tokens are available
*/
@Override
public int size() {
return this.map.size();
}
/**
* This method is used to acquire the value for all tokens that have
* currently been collected by this parser. This is used to determine which
* tokens have been extracted from the source. It is useful when the tokens
* have to be gathered.
*
* @return the list of value tokens that have been extracted
*/
@Override
public Collection<T> values() {
return this.map.values();
}
/**
* The <code>clear</code> method is used to wipe out all the currently
* existing tokens from the collection. This is used to recycle the parser
* so that it can be used to parse some other source of tokens without any
* lingering state.
*/
@Override
public void clear() {
this.all.clear();
this.map.clear();
}
}