diff --git a/Source/Processors/Channel/InfoObjects.cpp b/Source/Processors/Channel/InfoObjects.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d3beae8e0ee976d2fd6c0c4ddc31bcb7104c35f
--- /dev/null
+++ b/Source/Processors/Channel/InfoObjects.cpp
@@ -0,0 +1,397 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2014 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "InfoObjects.h"
+#include "../GenericProcessor/GenericProcessor.h"
+
+/** Common classes **/
+//Empty protected constructors
+HistoryObject::HistoryObject() {}
+NamedInfoObject::NamedInfoObject() {}
+
+//NodeInfoBase
+NodeInfoBase::NodeInfoBase(uint16 id) :
+	m_nodeID(id)
+{}
+
+unsigned int NodeInfoBase::getCurrentNodeID() const
+{
+	return m_nodeID;
+}
+
+//History Object
+String HistoryObject::getHistoricString()
+{
+	return m_historicString;
+}
+
+void HistoryObject::addToHistoricString(String entry)
+{
+	if (m_historicString.isEmpty())
+		m_historicString = entry;
+	else
+		m_historicString += (" -> " + entry);
+}
+
+//SourceProcessorInfo
+SourceProcessorInfo::SourceProcessorInfo(const GenericProcessor* source, uint16 subproc) 
+	:	m_sourceNodeID(source->nodeId),
+		m_sourceSubNodeIndex(subproc), 
+		m_sourceType(source->getName()),
+		m_sourceName(source->getName()) //TODO: fix those two when we have the ability to rename processors
+{
+}
+
+uint16 SourceProcessorInfo::getSourceNodeID() const
+{
+	return m_sourceNodeID;
+}
+
+uint16 SourceProcessorInfo::getSubProcessorIdx() const
+{
+	return m_sourceSubNodeIndex;
+}
+
+String SourceProcessorInfo::getSourceType() const
+{
+	return m_sourceType;
+}
+
+String SourceProcessorInfo::getSourceName() const
+{
+	return m_sourceName;
+}
+
+//NamedInfoObject
+void NamedInfoObject::setName(String name)
+{
+	m_name = name;
+}
+
+String NamedInfoObject::getName() const
+{
+	return m_name;
+}
+
+void NamedInfoObject::setDescriptor(String descriptor)
+{
+	m_descriptor = descriptor;
+}
+
+String NamedInfoObject::getDescriptor() const
+{
+	return m_descriptor;
+}
+
+void NamedInfoObject::setDescription(String description)
+{
+	m_description = description;
+}
+
+String NamedInfoObject::getDescription() const
+{
+	return m_description;
+}
+
+//InfoObjectCommon
+InfoObjectCommon::InfoObjectCommon(uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc)
+	:	NodeInfoBase(source->getNodeId()),
+		SourceProcessorInfo(source, subproc),
+		m_sourceIndex(idx),
+		m_sourceTypeIndex(typeidx)
+{
+}
+
+void InfoObjectCommon::setSampleRate(float sampleRate)
+{
+	m_sampleRate = sampleRate;
+}
+
+float InfoObjectCommon::getSampleRate() const
+{
+	return m_sampleRate;
+}
+
+
+uint16 InfoObjectCommon::getSourceIndex() const
+{
+	return m_sourceIndex;
+}
+
+uint16 InfoObjectCommon::getSourceTypeIndex() const
+{
+	return m_sourceTypeIndex;
+}
+
+//DataChannel
+
+DataChannel::DataChannel(DataChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc) :
+	InfoObjectCommon(idx, typeidx, source, subproc),
+	m_type(type)
+{
+}
+
+DataChannel::DataChannel(const DataChannel& ch)
+	:	InfoObjectCommon(ch), //Call default copy constructors of base classes
+		MetaDataInfoObject(ch),
+		HistoryObject(ch),
+		m_type(ch.m_type),
+		m_bitVolts(ch.m_bitVolts),
+		m_isEnabled(true),
+		m_isMonitored(false),
+		m_isRecording(false)
+{
+}
+
+DataChannel::~DataChannel()
+{
+}
+
+void DataChannel::setBitVolts(float bitVolts)
+{
+	m_bitVolts = bitVolts;
+}
+
+float DataChannel::getBitVolts() const
+{
+	return m_bitVolts;
+}
+
+DataChannel::DataChannelTypes DataChannel::getChannelType() const
+{
+	return m_type;
+}
+
+bool DataChannel::isEnabled() const
+{
+	return m_isEnabled;
+}
+
+void DataChannel::setEnable(bool e)
+{
+	m_isEnabled = e;
+}
+
+bool DataChannel::isMonitored() const
+{
+	return m_isMonitored;
+}
+
+void DataChannel::setMonitored(bool e)
+{
+	m_isMonitored = e;
+}
+
+void DataChannel::setRecordState(bool t)
+{
+	m_isRecording = t;
+}
+
+bool DataChannel::getRecordState() const
+{
+	return m_isRecording;
+}
+
+void DataChannel::reset()
+{
+	m_bitVolts = 1.0f;
+	m_isEnabled = true;
+	m_isMonitored = false;
+	m_isRecording = false;
+	setSampleRate(44100);
+}
+
+//EventChannel
+EventChannel::EventChannel(EventChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc)
+	:	InfoObjectCommon(idx, typeidx, source, subproc),
+		m_type(type)
+{
+}
+
+EventChannel::~EventChannel()
+{
+}
+
+EventChannel::EventChannelTypes EventChannel::getChannelType() const
+{
+	return m_type;
+}
+
+void EventChannel::setNumChannels(unsigned int numChannels)
+{
+	m_numChannels = numChannels;
+	//If event is TTL, set size to the needed number of bytes
+	if (m_type == TTL)
+	{
+		m_length = (numChannels + 7) / 8;
+		m_dataSize = m_length;
+	}
+}
+
+unsigned int EventChannel::getNumChannels() const
+{
+	return m_numChannels;
+}
+
+void EventChannel::setLength(unsigned int length)
+{
+	//Do nothing for TTLs, as the bytesize is fixed by the number of available channels
+	if (m_type == TTL) return;
+
+	m_length = length;
+	m_dataSize = length * getTypeByteSize(m_type);
+	//for messages, add 1 byte to account for the null terminator
+	if (m_type == MESSAGE) m_dataSize += 1;
+}
+
+unsigned int EventChannel::getLength() const
+{
+	return m_length;
+}
+
+size_t EventChannel::getDataSize() const
+{
+	return m_dataSize;
+}
+
+void EventChannel::setShouldBeRecorded(bool status)
+{
+	m_shouldBeRecorded = status;
+}
+
+bool EventChannel::getShouldBeRecorded() const
+{
+	return m_shouldBeRecorded;
+}
+
+size_t EventChannel::getTypeByteSize(EventChannel::EventChannelTypes type)
+{
+	switch (type)
+	{
+	case INT8_ARRAY: return sizeof(int8);
+	case UINT8_ARRAY: return sizeof(uint8);
+	case INT16_ARRAY: return sizeof(int16);
+	case UINT16_ARRAY: return sizeof(uint16);
+	case INT32_ARRAY: return sizeof(int32);
+	case UINT32_ARRAY: return sizeof(uint32);
+	case INT64_ARRAY: return sizeof(int64);
+	case UINT64_ARRAY: return sizeof(uint64);
+	default: return sizeof(char);
+	}
+}
+
+//SpikeChannel
+
+SpikeChannel::SpikeChannel(ElectrodeTypes type, uint16 idx, uint16 typeidx, GenericProcessor* source, const Array<const DataChannel*>& sourceChannels, uint16 subproc)
+	: InfoObjectCommon(idx, typeidx, source, subproc),
+	m_type(type)
+{
+	int n = sourceChannels.size();
+	jassert(n == getNumChannels(type));
+	for (int i = 0; i < n; i++)
+	{
+		sourceChannelInfo info;
+		const DataChannel* chan = sourceChannels[i];
+		info.processorID = chan->getSourceNodeID();
+		info.subProcessorID = chan->getSubProcessorIdx();
+		info.channelIDX = chan->getSourceIndex();
+		m_sourceInfo.add(info);
+	}
+}
+
+SpikeChannel::~SpikeChannel()
+{}
+
+SpikeChannel::ElectrodeTypes SpikeChannel::getChannelType() const
+{
+	return m_type;
+}
+
+Array<sourceChannelInfo> SpikeChannel::getSourceChannelInfo() const
+{
+	return m_sourceInfo;
+}
+
+void SpikeChannel::setGain(float gain)
+{
+	m_gain = gain;
+}
+
+float SpikeChannel::getGain() const
+{
+	return m_gain;
+}
+
+void SpikeChannel::setNumSamples(unsigned int preSamples, unsigned int postSamples)
+{
+	m_numPreSamples = preSamples;
+	m_numPostSamples = postSamples;
+}
+
+unsigned int SpikeChannel::getPrePeakSamples() const
+{
+	return m_numPreSamples;
+}
+
+unsigned int SpikeChannel::getPostPeakSamples() const
+{
+	return m_numPostSamples;
+}
+
+unsigned int SpikeChannel::getTotalSamples() const
+{
+	return m_numPostSamples + m_numPreSamples;
+}
+
+unsigned int SpikeChannel::getNumChannels() const
+{
+	return getNumChannels(m_type);
+}
+
+unsigned int SpikeChannel::getNumChannels(SpikeChannel::ElectrodeTypes type)
+{
+	switch (type)
+	{
+	case SINGLE: return 1;
+	case STEREOTRODE: return 2;
+	case TETRODE: return 4;
+	default: return 1;
+	}
+}
+
+//ConfigurationObject
+ConfigurationObject::ConfigurationObject(String descriptor, GenericProcessor* source, uint16 subproc)
+	: SourceProcessorInfo(source, subproc)
+{
+	setDescriptor(descriptor);
+}
+
+void ConfigurationObject::setShouldBeRecorded(bool status)
+{
+	m_shouldBeRecorded = status;
+}
+
+bool ConfigurationObject::getShouldBeRecorded() const
+{
+	return m_shouldBeRecorded;
+}
\ No newline at end of file
diff --git a/Source/Processors/Channel/InfoObjects.h b/Source/Processors/Channel/InfoObjects.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cf7eb8f34bd3aabe7788a00691a22c7f10b24ee
--- /dev/null
+++ b/Source/Processors/Channel/InfoObjects.h
@@ -0,0 +1,416 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2014 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef INFOOBJECTS_H_INCLUDED
+#define INFOOBJECTS_H_INCLUDED
+
+#include "../../../JuceLibraryCode/JuceHeader.h"
+#include "../PluginManager/OpenEphysPlugin.h"
+#include "MetaData.h"
+
+class GenericProcessor;
+
+// ------- Ancilliary objects -------//
+
+/**
+Structure with the basic info that identifies a channel
+*/
+struct sourceChannelInfo
+{
+	uint16 processorID;
+	uint16 subProcessorID;
+	uint16 channelIDX;
+};
+
+class PLUGIN_API NodeInfoBase
+{
+	//This field should never be changed by anything except GenericProcessor base code
+	friend class GenericProcessor;
+public:
+	/** Gets the ID of the processor which currently owns this copy of the info object */
+	unsigned int getCurrentNodeID() const;
+protected:
+	NodeInfoBase() = delete;
+	NodeInfoBase(uint16 id);
+	uint16 m_nodeID{ 0 };
+};
+
+/** This class allows creating a string with an historic of all the data a node has gone through */
+class PLUGIN_API HistoryObject
+{
+protected:
+	HistoryObject();
+
+public:
+	/** Returns the historic string */
+	String getHistoricString();
+	/** Adds a new entry in the historic string*/
+	void addToHistoricString(String entry);
+
+private:
+	String m_historicString;
+};
+
+class PLUGIN_API SourceProcessorInfo
+{
+protected:
+	SourceProcessorInfo(const GenericProcessor* source, uint16 subproc = 0);
+
+public:
+	/** Gets the ID of the processor which created the channel object */
+	uint16 getSourceNodeID() const;
+
+	/** Gets the subprocessor index associated to this channel object*/
+	uint16 getSubProcessorIdx() const;
+
+	/** Gets the processor type of the node which created this object */
+	String getSourceType() const;
+
+	/** Gets the name of the processor which created this object */
+	String getSourceName() const;
+
+private:
+	SourceProcessorInfo() = delete;
+	const uint16 m_sourceNodeID;
+	const uint16 m_sourceSubNodeIndex;
+	const String m_sourceType;
+	const String m_sourceName;
+
+};
+
+class PLUGIN_API NamedInfoObject
+{
+public:
+	/** Sets the object's name*/
+	void setName(String name);
+
+	/** Returns the name of a given object*/
+	String getName() const;
+
+	/** Sets the Channel Info Object description, to be stored in most file formats*/
+	void setDescription(String description);
+
+	/** Gets the Channel Info Object description */
+	String getDescription() const;
+
+	/** Sets a machine-readable data descriptor (eg.: data.continuous.headstage ) */
+	void setDescriptor(String descriptor);
+
+	String getDescriptor() const;
+protected:
+	NamedInfoObject();
+private:
+	String m_name;
+	String m_descriptor;
+	String m_description;
+
+};
+
+/** Common class for all info objects */
+class PLUGIN_API InfoObjectCommon :
+	public NodeInfoBase, public SourceProcessorInfo, NamedInfoObject
+{
+protected:
+	InfoObjectCommon(uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0);
+
+public:
+
+	/** Sets the sample rate value for this channel. */
+	void setSampleRate(float sampleRate);
+
+	/** Returns the sample rate value for this channel. */
+	float getSampleRate() const;
+
+
+	/** Gets the position of this channel in the source processor*/
+	uint16 getSourceIndex() const;
+
+	/** Gets the position in the source processor of this channel object, relative
+	to its subtype (HEADSTAGE, AUX or ADC for data channels, TTL, MESSAGE or BINARY for events, etc...) */
+	uint16 getSourceTypeIndex() const;
+
+private:
+	/** Index of the object in the source processor */
+	const uint16 m_sourceIndex;
+	/** Index of this particular subtype in the source processor */
+	const uint16 m_sourceTypeIndex;
+
+	float m_sampleRate{ 44100.0f };
+};
+
+// ------- Main objects -------//
+
+class PLUGIN_API DataChannel
+	: public InfoObjectCommon, public MetaDataInfoObject, public HistoryObject
+{
+public:
+
+	enum DataChannelTypes
+	{
+		HEADSTAGE_CHANNEL = 0,
+		AUX_CHANNEL = 1,
+		ADC_CHANNEL = 2
+	};
+	//--------- CONSTRUCTOR / DESTRUCTOR --------//
+
+	/** Default constructor for creating Channels from scratch.
+		@param type The type of data this channel represents (HEADSTAGE, ADC, AUX)
+		@param idx The index of this data channel in the source processor
+		@param typeidx The index of this particular type of data channel in the source processor
+		@param source A pointer to the source processor
+		@param subproc Optional. The source subprocessor index.
+	*/
+	DataChannel(DataChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0);
+
+	/** Copy constructor. */
+	DataChannel(const DataChannel& ch);
+
+	~DataChannel();
+
+	//--------- DATA GET / SET METHODS --------//
+
+	/** Sets the bitVolts value for this channel. */
+	void setBitVolts(float bitVolts);
+
+	/** Returns the bitVolts value for this channel. */
+	float getBitVolts() const;
+
+	DataChannelTypes getChannelType() const;
+
+	//--------- STATUS METHODS ----------//
+	/** Toggled when a channel is disabled from further processing. */
+	bool isEnabled() const;
+
+	/** Toggled when a channel is disabled from further processing. */
+	void setEnable(bool e);
+
+	/** Informs whether the channel is being routed to the audio node */
+	bool isMonitored() const;
+
+	/** Sets if the channel needs to be routed to the audio node */
+	void setMonitored(bool e);
+
+	//Record methods will be phased out with the probe-based record system
+	/** Sets whether or not the channel will record. */
+	void setRecordState(bool t);
+
+	/** Informs whether or not the channel will record. */
+	bool getRecordState() const;
+
+	//---------- OTHER METHODS ------------//
+	/** Restores the default settings for a given channel. */
+	void reset();
+
+private:
+	const DataChannelTypes m_type;
+	float m_bitVolts{ 1.0f };
+	bool m_isEnabled{ true };
+	bool m_isMonitored{ false };
+	bool m_isRecording{ false };
+
+	JUCE_LEAK_DETECTOR(DataChannel);
+};
+
+class PLUGIN_API EventChannel :
+	public InfoObjectCommon, public MetaDataInfoObject, public MetaDataEventObject
+{
+public:
+	enum EventChannelTypes
+	{
+		//Numeration kept to maintain compatibility with old code
+		TTL = 3,
+		MESSAGE = 5,
+		//generic binary types. These will be treated by the majority of record engines as simple binary blobs,
+		//while having strict typing helps creating stabler plugins
+		INT8_ARRAY = 10,
+		UINT8_ARRAY,
+		INT16_ARRAY,
+		UINT16_ARRAY,
+		INT32_ARRAY,
+		UINT32_ARRAY,
+		INT64_ARRAY,
+		UINT64_ARRAY,
+		FLOAT_ARRAY,
+		DOUBLE_ARRAY
+	};
+
+	/** Default constructor
+	@param type The type of event this channel represents (TTL, MESSAGE, BYINARY_MSG)
+	@param idx The index of this event channel in the source processor
+	@param typeidx The index of this particular type of event channel in the source processor
+	@param source A pointer to the source processor
+	@param subproc Optional. The source subprocessor index.
+	*/
+	EventChannel(EventChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0);
+
+	~EventChannel();
+
+	EventChannelTypes getChannelType() const;
+
+	/** Sets the number of virtual channels this event can carry
+		-For TTL signals, it must be the number of bits in the TTL word.
+		-For other events, this might be used to differentiate between different origins within the same processor
+	*/
+	void setNumChannels(unsigned int numChannels);
+
+	/** Gets the number of virtual channels
+		@see setNumChannels
+	*/
+	unsigned int getNumChannels() const;
+
+	/** Sets the length of the event payload
+		-For TTL signals, this method will do nothing, as the size is fixed by the number of ttl channels
+		-For message events, the length of the string in characters
+		-For typed array events, the number of elements
+	*/
+	void setLength(unsigned int length);
+
+	/** Gets the size of the event payload
+		-For TTL, it returns the number of bytes that form the full TTL word, same as getByteDataSize()
+		-For message events, the number of characters
+		-For typed array events, the number of elements.
+	*/
+	unsigned int getLength() const;
+
+	/** Gets the size of the event payload in bytes*/
+	size_t getDataSize() const;
+	
+	/** Sets if the event should be recorded or not.
+		Note that this option does not prevent the event from actually being recorded, it simply states
+		a processor's developer opinion on how the event should be treated, but can be overrided by the user.
+
+		Meant for events that represent runtime changes in the state of a processor that can be useful to
+		other processors but hold no actual meaning for the experiment.
+	*/
+	void setShouldBeRecorded(bool status);
+
+	/** Gets the event preference about being recorded */
+	bool getShouldBeRecorded() const;
+
+	/** Gets the size in bytes of an element depending of the type*/
+	static size_t getTypeByteSize(EventChannelTypes type);
+
+private:
+	const EventChannelTypes m_type;
+	unsigned int m_numChannels{ 1 };
+	size_t m_dataSize{ 1 };
+	unsigned int m_length{ 1 };
+	bool m_shouldBeRecorded{ true };
+
+	JUCE_LEAK_DETECTOR(EventChannel);
+};
+
+class PLUGIN_API SpikeChannel :
+	public InfoObjectCommon, public MetaDataInfoObject, public MetaDataEventObject
+{
+public:
+	enum ElectrodeTypes
+	{
+		SINGLE,
+		STEREOTRODE,
+		TETRODE
+	};
+
+	/** Default constructor 
+		@param type The type of electrode this channel represents (SINGLE, STEREOTRODE, TETRODE)
+		@param idx The index of this spike channel in the source processor
+		@param typeidx The index of this particular type of spike channel in the source processor
+		@param source A pointer to the source processor
+		@param souceChannels An array containing const pointers to the channels that originate the data for this spike electrode
+		@param subproc Optional. The source subprocessor index.
+	*/
+	SpikeChannel(ElectrodeTypes type, uint16 idx, uint16 typeidx, GenericProcessor* source, const Array<const DataChannel*>& sourceChannels, uint16 subproc = 0);
+
+	~SpikeChannel();
+
+	ElectrodeTypes getChannelType() const;
+
+	/** Returns an array with info about the channels from which the spikes originate */
+	Array<sourceChannelInfo> getSourceChannelInfo() const;
+
+	/** Sets the electrode gain */
+	void setGain(float gain);
+
+	/** Gets the electrode gain */
+	float getGain() const;
+
+	/** Sets the number of samples, pre and post peak */
+	void setNumSamples(unsigned int preSamples, unsigned int postSamples);
+
+	/** Gets the number of pre peak samples */
+	unsigned int getPrePeakSamples() const;
+
+	/** Gets the number of post peak samples */
+	unsigned int getPostPeakSamples() const;
+
+	/** Gets the total number of samples */
+	unsigned int getTotalSamples() const;
+
+	/** Gets the number of channels associated with the electrode type */
+	unsigned int getNumChannels() const;
+
+	/** Gets the number of channels associated with a specific electrode type */
+	static unsigned int getNumChannels(ElectrodeTypes type);
+
+private:
+	const ElectrodeTypes m_type;
+	Array<sourceChannelInfo> m_sourceInfo;
+	float m_gain{ 1.0f };
+	unsigned int m_numPreSamples{ 8 };
+	unsigned int m_numPostSamples{ 32 };
+
+	JUCE_LEAK_DETECTOR(DataChannel);
+};
+
+/**
+	Class for defining extra configuration objects to be shared with processors down the chain not associated with any particular channel or event.
+	It does not hold any data by itself, but can be filled with metadata fields to form any structure
+*/
+class PLUGIN_API ConfigurationObject :
+	public SourceProcessorInfo, public NamedInfoObject, public MetaDataInfoObject
+{
+public:
+	/**Default constructor
+	@param descriptor the descriptor field of NamedInfoObject, required for config objects
+	@param source The source processor
+	@param subproc Optional. The source subprocessor index
+	*/
+	ConfigurationObject(String descriptor, GenericProcessor* source, uint16 subproc = 0);
+
+	/** Sets if the configuration should be recorded or not.
+	Similar to the events, this does not prevent the configuration data to be recorded, but rather states
+	a preference by the source developer.
+	*/
+	void setShouldBeRecorded(bool status);
+
+	/** Gets the config preference about being recorded */
+	bool getShouldBeRecorded() const;
+
+private:
+	bool m_shouldBeRecorded{ true };
+	JUCE_LEAK_DETECTOR(ConfigurationObject);
+};
+
+
+
+#endif
\ No newline at end of file