/*******************************************************************************
 * Copyright (c) 2001, 2013 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.sse.core.internal.provisional;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.encoding.EncodingRule;
import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceAlreadyExists;
import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.util.URIResolver;

/**
 * <p>
 * Provides APIs for managing (get, release, save, and save as) SSE Structured
 * Models.
 * </p>
 * <p>
 * Structured Models created from an implementor of this interface can be
 * either managed or unmanaged. Managed models are shared using reference
 * counts, so until that count has been decremented to zero, the model will
 * continue to exist in memory. When managed, models can be looked up using
 * their IDs or their IStructuredDocuments, which can be advantageous when
 * building on APIs that aren't specifically designed for SSE (such as those
 * revolving around IDocuments). Unmanaged models offer no such features, and
 * are largely used for tasks where their contents are ephemeral, such as for
 * populating a source viewer with syntax-colored content.  Both types of models
 * must be released when no longer needed.
 * </p>
 * <p>
 * There are two types of access used when retrieving a model from the model
 * manager: READ and EDIT. The contents of a model can be modified regardless
 * of which access type is used, but any client who gets a model for EDIT is
 * explicitly declaring that they are interested in saving those changed
 * contents. The EDIT and READ reference counts are visible to everyone, as
 * are convenience methods for determining whether a managed model is shared
 * among multiple clients accessing it for READ or EDIT.
 * </p>
 * <p>
 * Managed models whose contents are "dirty" with READ and EDIT counts above
 * zero will be reverted to the on-disk content if the EDIT count drops to
 * zero while the READ count remains above zero.
 * </p>
 * <p>
 * Shared models for which the read and edit counts have both dropped to zero
 * are no longer valid for use, regardless of whether they have been garbage
 * collected or not. It is possible, but not guaranteed, that the underlying
 * structured document is still valid and may even be used in constructing a
 * new shared model.
 * </p>
 * <p>
 * 
 * @noimplement This interface is not intended to be implemented by clients.
 * Clients should obtain an instance of the IModelManager interface through
 * {@link StructuredModelManager#getModelManager()}.</p>
 * <p>@see {@link StructuredModelManager}</p>
 */
public interface IModelManager {

	/**
	 * A fixed ID used for models which were created as duplicates of existing
	 * models
	 */
	public final static String DUPLICATED_MODEL = "org.eclipse.wst.sse.core.IModelManager.DUPLICATED_MODEL"; //$NON-NLS-1$

	/**
	 * A fixed ID used for unmanaged models
	 */
	public final static String UNMANAGED_MODEL = "org.eclipse.wst.sse.core.IModelManager.UNMANAGED_MODEL"; //$NON-NLS-1$

	/**
	 * Calculate id provides a common way to determine the id from the input
	 * ... needed to get and save the model. It is a simple class utility, but
	 * is an instance method so can be accessed via interface.
	 */
	public String calculateId(IFile file);

	/**
	 * Copies a model with the old id 
	 * @param oldId - the old model's ID
	 * @param newId - the new model's ID
	 * @return the new model
	 * @throws ResourceInUse if the given new ID is already in use by a managed model
	 */
	IStructuredModel copyModelForEdit(String oldId, String newId) throws ResourceInUse;

	/**
	 * Creates a new, but empty, unmanaged model of the same kind as the one
	 * given. For a managed model with the same contents, use "copy".
	 * 
	 * @param model
	 * @return the model, or null of one could not be created
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 */
	public IStructuredModel createNewInstance(IStructuredModel model) throws IOException;

	/**
	 * Factory method, since a proper IStructuredDocument must have a proper
	 * parser assigned. If the resource does already exist, then
	 * createStructuredDocumentFor is the right API to use.
	 * 
	 * @param iFile
	 * @return the document, or null if one could not be created
	 * @throws ResourceAlreadyExists
	 *             if the IFile already exists
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 * @throws ResourceAlreadyExists if the give file already exists
	 */
	IStructuredDocument createNewStructuredDocumentFor(IFile iFile) throws ResourceAlreadyExists, IOException, CoreException;

	/**
	 * Factory method, since a proper IStructuredDocument must have a proper
	 * parser assigned. Note: clients should verify IFile exists before using
	 * this method. If this IFile does not exist, then
	 * {@link #createNewStructuredDocumentFor(IFile)} is the correct API to use.
	 * 
	 * @param iFile - the file
	 * @return the document, or null if one could not be created
	 * 
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 */
	IStructuredDocument createStructuredDocumentFor(IFile iFile) throws IOException, CoreException;

	/**
	 * Convenience method, since a proper IStructuredDocument must have a
	 * proper parser assigned. It should only be used when an empty
	 * structuredDocument is needed. Otherwise, use IFile form.
	 * 
	 * @param contentTypeId
	 * @return a structured document with the correct parsing setup for the
	 *         given content type ID, or null if one could not be created or
	 *         the given content type ID is unknown or unsupported
	 */
	IStructuredDocument createStructuredDocumentFor(String contentTypeId);

	/**
	 * @deprecated - use IFile form instead as the correct encoding and content rules may not be applied otherwise
	 * 
	 * Creates and returns a properly configured structured document for the given contents with the given name
	 * 
	 * @param filename - the filename, which may be used to guess the content type
	 * @param contents - the contents to load
	 * @param resolver - the URIResolver to use for locating any needed resources
	 * @return the IStructuredDocument or null of one could not be created
	 * @throws IOException if the file's contents can not be read or its content type can not be determined
	 */ 
	IStructuredDocument createStructuredDocumentFor(String filename, InputStream contents, URIResolver resolver) throws IOException;

	/**
	 * Creates and returns a properly configured structured document for the given contents with the given name
	 * 
	 * @param filename - the filename, which may be used to guess the content type
	 * @param inputStream - the contents to load
	 * @param resolver - the URIResolver to use for locating any needed resources
	 * @param ianaEncodingName - the IANA specified encoding to use when reading the contents
	 * @return the IStructuredDocument or null if one could not be created
	 * @throws IOException if the file's contents can not be read or its content type can not be determined
	 * @deprecated - clients should convert the InputStream into text themselves
	 *             and then use the version of this method taking a String for its
	 *             content
	 */
	IStructuredDocument createStructuredDocumentFor(String filename, InputStream inputStream, URIResolver resolver, String ianaEncodingName) throws IOException;

	/**
	 * Creates and returns a properly configured structured document for the given contents with the given name
	 * 
	 * @param filename - the filename, which may be used to guess the content type
	 * @param content - the contents to load
	 * @param resolver - the URIResolver to use for locating any referenced resources
	 * @return a structured document with the correct parsing setup for the
	 *         given filename, or null if one could not be created
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 */
	IStructuredDocument createStructuredDocumentFor(String filename, String content, URIResolver resolver) throws IOException;

	/**
	 * <p>Creates and returns an unmanaged model populated with the given IFile's
	 * contents.</p>
	 * <p>
	 * {@link IStructuredModel#releaseFromRead()} or
	 * {@link IStructuredModel#releaseFromEdit()} should still be called directly
	 * on returned instances to properly dispose of them.
	 * </p>
	 * 
	 * @param iFile
	 * @return a structured model, or null if one could not be created
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 */
	IStructuredModel createUnManagedStructuredModelFor(IFile iFile) throws IOException, CoreException;

	/**
	 * <p>Creates and returns an unmanaged model populated with the given IFile's
	 * contents.</p>
	 * <p>
	 * {@link IStructuredModel#releaseFromRead()} or
	 * {@link IStructuredModel#releaseFromEdit()} should still be called directly
	 * on returned instances to properly dispose of them.
	 * </p>
	 * 
	 * @param contentTypeId
	 * @return a structured model for the given content type, or null if one
	 *         could not be created or the content type is unsupported
	 */
	IStructuredModel createUnManagedStructuredModelFor(String contentTypeId);

	/**
	 * @deprecated
	 */
	IStructuredModel createUnManagedStructuredModelFor(String contentTypeId, URIResolver resolver);

	/**
	 * Note: callers of this method must still release the model when finished.
	 * 
	 * @param document
	 * @return the structured model containing the give document, incrementing
	 *         its edit count, or null if there is not a model corresponding
	 *         to this document.
	 */
	IStructuredModel getExistingModelForEdit(IDocument document);

	/**
	 * @param file
	 * @return the structured model for the given file, incrementing its edit
	 *         count, or null if one does not already exist for this file.
	 */
	IStructuredModel getExistingModelForEdit(IFile file);

	/**
	 * @param id
	 * @return the structured model with the given ID, incrementing its edit
	 *         count, or null if one does not already exist for this ID
	 */
	public IStructuredModel getExistingModelForEdit(Object id);

	/**
	 * Note: callers of this method must still release the model when finished.
	 * 
	 * @param document
	 * @return the structured model containing the give document, incrementing
	 *         its read count, or null if there is not a model corresponding
	 *         to this document.
	 */
	IStructuredModel getExistingModelForRead(IDocument document);

	/**
	 * @param file
	 * @return the structured model for the given file, incrementing its read
	 *         count, or null if one does not already exist for this file.
	 */
	public IStructuredModel getExistingModelForRead(IFile iFile);

	/**
	 * @param id
	 * @return the structured model with the given ID, incrementing its edit
	 *         count, or null if one does not already exist for this ID
	 */
	public IStructuredModel getExistingModelForRead(Object id);

	/**
	 * @deprecated - internal information
	 */
	public Enumeration getExistingModelIds();

	/**
	 * Returns a structured model for the given file. If one does not already
	 * exists, one will be created with an edit count of 1. If one already
	 * exists, its edit count will be incremented before it is returned.
	 * 
	 * @param iFile
	 * @return a structured model for the given file, or null if one could not
	 *         be found or created
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 */
	public IStructuredModel getModelForEdit(IFile iFile) throws IOException, CoreException;

	/**
	 * Returns a structured model for the given file. If one does not already
	 * exists, one will be created with an edit count of 1. If one already
	 * exists, its edit count will be incremented before it is returned.
	 * 
	 * @param iFile
	 * @param encodingRule the rule for handling encoding
	 * @return a structured model for the given file, or null if one could not
	 *         be found or created
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 * @deprecated - encoding is handled automatically based on the file's
	 *             contents or user preferences
	 */
	public IStructuredModel getModelForEdit(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException;

	/**
	 * @deprecated - Encoding and the line delimiter used are handled
	 *             automatically based on the file's contents or user
	 *             preferences.
	 */
	public IStructuredModel getModelForEdit(IFile iFile, String encoding, String lineDelimiter) throws UnsupportedEncodingException, IOException, CoreException;

	/**
	 * Returns a structured model for the given document. If one does not
	 * already exists, one will be created with an edit count of 1. If one
	 * already exists, its edit count will be incremented before it is
	 * returned. This method is intended only to interact with documents
	 * contained within File Buffers.
	 * 
	 * @param textFileBufferDocument
	 * @return a structured model for the given document, or null if there is
	 *         insufficient information known about the document instance to
	 *         do so
	 */
	public IStructuredModel getModelForEdit(IStructuredDocument textFileBufferDocument);

	/**
	 * Returns a structured model for the given contents using the given ID.
	 * If one does not already exist, one will be created with an edit count
	 * of 1. If one already exists, its edit count will be incremented before
	 * it is returned.
	 * 
	 * @param id
	 *            - the id for the model
	 * @param inStream
	 *            - the initial contents of the model
	 * @param resolver
	 *            - the URIResolver to use for locating any needed resources
	 * @return a structured model for the given content, or null if one could
	 *         not be found or created
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 *             if the contents can not be read or its detected encoding
	 *             does not support its contents
	 * @deprecated - a URI resolver should be automatically created when
	 *             needed
	 */
	public IStructuredModel getModelForEdit(String id, InputStream inStream, URIResolver resolver) throws UnsupportedEncodingException, IOException;

	/**
	 * Returns a structured model for the given file. If one does not already
	 * exists, one will be created with a read count of 1. If one already
	 * exists, its read count will be incremented before it is returned.
	 * 
	 * @param iFile
	 * @return a structured model for the given file, or null if one could not
	 *         be found or created
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 */
	public IStructuredModel getModelForRead(IFile iFile) throws IOException, CoreException;

	/**
	 * @deprecated - encoding is handled automatically based on the file's
	 *             contents or user preferences
	 */
	public IStructuredModel getModelForRead(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException;

	/**
	 * @deprecated - Encoding and the line delimiter used are handled
	 *             automatically based on the file's contents or user
	 *             preferences.
	 */
	public IStructuredModel getModelForRead(IFile iFile, String encoding, String lineDelimiter) throws UnsupportedEncodingException, IOException, CoreException;

	/**
	 * Returns a structured model for the given document. If one does not
	 * already exists, one will be created with a read count of 1. If one
	 * already exists, its read count will be incremented before it is
	 * returned. This method is intended only to interact with documents
	 * contained within File Buffers.
	 * 
	 * @param textFileBufferDocument
	 * @return a structured model for the given document, or null if there is
	 *         insufficient information known about the document instance to
	 *         do so
	 */
	public IStructuredModel getModelForRead(IStructuredDocument textFileBufferDocument);

	/**
	 * Returns a structured model for the given contents using the given ID.
	 * If one does not already exist, one will be created with an read count
	 * of 1. If one already exists, its read count will be incremented before
	 * it is returned.
	 * 
	 * @param id
	 *            - the id for the model
	 * @param inStream
	 *            - the initial contents of the model
	 * @param resolver
	 *            - the URIResolver to use for locating any needed resources
	 * @return a structured model for the given content, or null if one could
	 *         not be found or created
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 *             if the contents can not be read or its detected encoding
	 *             does not support its contents
	 * @deprecated - a URI resolver should be automatically created when
	 *             needed
	 */
	public IStructuredModel getModelForRead(String filename, InputStream inStream, URIResolver resolver) throws UnsupportedEncodingException, IOException;

	/**
	 * This method will not create a new model if it already exists ... if
	 * force is false. The idea is that a client should call this method once
	 * with force set to false. If the exception is thrown, then prompt client
	 * if they want to overwrite.
	 * 
	 * @param iFile
	 * @param force
	 * @return the new structured model, or 
	 * @throws ResourceInUse if the given new ID is already in use by a managed model
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 * @throws ResourceAlreadyExists if the give file already exists
	 */
	IStructuredModel getNewModelForEdit(IFile iFile, boolean force) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException;

	/**
	 * This method will not create a new model if it already exists ... if
	 * force is false. The idea is that a client should call this method once
	 * with force set to false. If the exception is thrown, then prompt client
	 * if they want to overwrite.
	 * 
	 * @param iFile
	 * @param force
	 * @return the new structured model, or 
	 * @throws ResourceInUse if the given new ID is already in use by a managed model
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 * @throws ResourceAlreadyExists if the give file already exists
	 */
	IStructuredModel getNewModelForRead(IFile iFile, boolean force) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException;

	/**
	 * This function returns the combined "read" and "edit" reference counts
	 * of underlying model.
	 * 
	 * @param id
	 *            Object The id of the model
	 * @deprecated - internal information that can be obtained from the model
	 *             itself
	 */
	int getReferenceCount(Object id);

	/**
	 * This function returns the "edit" reference count of underlying model.
	 * 
	 * @param id
	 *            Object The id of the model
	 * @deprecated - internal information that can be obtained from the model itself
	 */
	int getReferenceCountForEdit(Object id);

	/**
	 * This function returns the "read" reference count of underlying model.
	 * 
	 * @param id
	 *            Object The id of the model TODO: try to refine the design
	 *            not to use this function
	 * @deprecated - internal information that can be obtained from the model itself
	 */
	int getReferenceCountForRead(Object id);

	/**
	 * This function returns true if there are other references to the
	 * underlying model.
	 * 
	 * @param id
	 *            Object The id of the model
	 */
	boolean isShared(Object id);

	/**
	 * This function returns true if there are other "edit" references to the
	 * underlying model.
	 * 
	 * @param id
	 *            Object The id of the model
	 */
	boolean isSharedForEdit(Object id);

	/**
	 * This function returns true if there are other "read" references to the
	 * underlying model.
	 * 
	 * @param id
	 *            Object The id of the model
	 */
	boolean isSharedForRead(Object id);

	/**
	 * @deprecated - not granular enough
	 * 
	 * This method can be called to determine if the model manager is within a
	 * "aboutToChange" and "changed" sequence.
	 */
	public boolean isStateChanging();

	/**
	 * This method changes the id of the model. 
	 * 
	 * TODO: try to refine the design
	 * not to use this function
	 * 
	 * @deprecated
	 */
	void moveModel(Object oldId, Object newId);

	/**
	 * This method can be called when the content type of a model changes. It's
	 * assumed the contentType has already been changed, and this method uses
	 * the text of the old one, to repopulate the text of the new one. In
	 * theory, the actual instance could change, (e.g. using 'saveAs' to go
	 * from xml to dtd), but in practice, the intent of this API is to return
	 * the same instance, just using different handlers, adapter factories,
	 * etc.
	 */
	IStructuredModel reinitialize(IStructuredModel model) throws IOException;

	/**
	 * This is similar to the getModel method, except this method does not use
	 * the cached version, but forces the cached version to be replaced with a
	 * fresh, unchanged version. Note: this method does not change any
	 * reference counts. Also, if there is not already a cached version of the
	 * model, then this call is essentially ignored (that is, it does not put
	 * a model in the cache) and returns null.
	 * 
	 * @deprecated
	 */
	IStructuredModel reloadModel(Object id, InputStream inStream) throws UnsupportedEncodingException;

	/**
	 * Saves the contents of the given structured document to the given file. If
	 * the document belongs to a managed model, that model will be saved and
	 * marked as non-dirty.
	 * 
	 * @param structuredDocument
	 *            - the structured document
	 * @param iFile
	 *            - the file to save to
	 * @throws UnsupportedEncodingException
	 * @throws CoreException if the file's contents or description can not be read
	 * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents
	 */
	void saveStructuredDocument(IStructuredDocument structuredDocument, IFile iFile) throws UnsupportedEncodingException, IOException, CoreException;
}
