📄 contentstorage.java
字号:
/*MujMail - Simple mail client for J2MECopyright (C) 2008 David Hauzar <david.hauzar.mujmail@gmail.com>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, bufferContent to the Free SoftwareFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */package mujmail;import mujmail.util.Decode;import mujmail.util.OutputBuffer;import mujmail.protocols.MailSender;import mujmail.connections.ConnectionInterface;import java.io.DataInputStream;import java.io.DataOutputStream;import mujmail.util.Functions;/** * Persistently stores the content of body part. Part of * <code>BodyPart</code>. * * It is possible to incrementally add the content to existing * content without keeping existent content in heap memory. This is * important for example when reading the content from SMTP connection. * * Provides methods for adding content to the storage, for * buffered adding content to the storage - this case the content * is stored when more content for storing is collected - as well * as methods for reading or deleting the content. * Than provides information about the content contained in this * storage. * * Object of this type is contained in object {@link BodyPart}. * * This object is stored persistently in RMS database. It's loading * and saving does object of instance {@link BodyPart} that * contains this storage. * * @author David Hauzar */public abstract class ContentStorage { /** Flag signals if we want to print debug prints */ private static final boolean DEBUG = false; public static final String SOURCE_FILE = "ContentStorage"; //private static final long MAX_SIZE_OF_BODYPART = Long.MAX_VALUE; private long size = 0; private BodyPart bodyPart; private final BufferedContentAdder buffer = new BufferedContentAdder(); /** * * @param bodyPart the body part which content is stored in this storage */ protected ContentStorage(BodyPart bodyPart){ this.bodyPart = bodyPart; } /** * Gets the name of the bodypart which content this storage holds. * @return the name of the bodypart that content this storage holds. */ protected String getName() { return bodyPart.getHeader().getName(); } /** * * @param bodyPart the body part which content is stored in this storage * @param size the size of the content stored in the storage */ protected ContentStorage(BodyPart bodyPart, long size) { this.bodyPart = bodyPart; this.size = size; } /** * It is important to lower the likelihood of throwing OutOfMemory exception * while calling method addToContent or addToContentRaw. This means that * allocating objects in these methods is undesirable. This method should * preallocate objects needed in addToContent or addToContentRaw. * * It is ensured that this method will be called after saving the content * to the storage and releasing the data kept in the buffer so it is * unlikely to OutOfMemoryException to be thrown there. */ protected abstract void preallocateToNextSaving(); /** * * @return true if the content is yet preallocated to next saving. */ protected abstract boolean preallocatedToNextSaving(); /** * Ensures that te objects are really preallocated before calling addToContent * or addToContentRaw. */ private void ensurePrealocating() { if (!preallocatedToNextSaving()) { preallocateToNextSaving(); } } /** * Initialize class by copying another instance. * If copyMode is DEEP_COPY copies the content. * Note that if the mode is SHALLOW_COPY it the body part which content * will is stored in this ContentStorage should be in the same box as original * storage. * @param bp the body part which content is stored in this storage * @param copy AttachmentPart instance to copy * @param copyMode defines copying mode */ protected ContentStorage(BodyPart bodyPart, ContentStorage copy, CopyingModes copyMode) { this.bodyPart = bodyPart; if (copyMode == CopyingModes.NO_COPY) { // the instance is yet created return; } if (copyMode == CopyingModes.DEEP_COPY) { addContent(copy, false); return; } } /** * Creates new instance of storage of type identified with given * number. * @param storageTypeNumber the number which identify the storage type * of which we want to create an instance * @return the storage of given type */ public static ContentStorage createStorageInstance(BodyPart bodyPart, byte storageTypeNumber) { //#ifdef MUJMAIL_FS if (storageTypeNumber == StorageTypes.FS_STORAGE.getStorageTypeNumber()) { return new FSStorage(bodyPart); } //#endif if (storageTypeNumber == StorageTypes.RMS_STORAGE.getStorageTypeNumber()) { return new RMSStorage(bodyPart); } throw new RuntimeException("not implemented storage type"); } /** * Gets the body part which content is stored in the storage. * @return */ protected BodyPart getBodyPart() { return bodyPart; } /** * Returns the storage type of given storage * @return */ public abstract StorageTypes getStorageType(); /** * Returns new instance of storage which is copy of this instance * of the storage. * @return new instance of storage which is copy of this instance */ public abstract ContentStorage copy(BodyPart bp, CopyingModes copyMode); /** * Checks whether it is allowed to make a copy of this instance with given * copy mode. * @param bp * @param copyMode */ protected boolean checkCopy(BodyPart bp, CopyingModes copyMode) { if (copyMode == CopyingModes.SHALLOW_COPY && bp.getBox() != getBodyPart().getBox()) { return false; } return true; } /** * Loads information about this storage from input stream (of RMS database). * Does not load the content of storage, loads only information about * the storage. * @param inputStream the input stream in which are stored information * about this storage. * @throws java.lang.Exception can occur while reading inputStream */ public void loadStorage(DataInputStream inputStream) throws Exception { setSize( inputStream.readLong() ); } /** * Saves information about this storage to output stream (RMS database) * Does not save the content of the storage, saves only information about * this storage. * @param outputStream the output stream to which the information about * this body part will be saved * @throws java.lang.Exception can occur while writing to the outputStream */ public void saveStorageHeader(DataOutputStream outputStream) throws Exception { outputStream.writeLong(getSize()); } /** * Deletes the content of this bodypart. */ public void deleteContent() { setSize(0); buffer.clearCache(); if (!bodyPart.convertedContentMode()) { bodyPart.setBodyState(BodyPart.BS_EMPTY); } deleteContentFromStorage(); } /** * Deletes the content from storage. Called from method * {@link #deleteContent()}}. */ protected abstract void deleteContentFromStorage(); public void addContent(ContentStorage copy, boolean safeMode) { // TODO: redefine this method in class RMS storage // in case of RMS store it can be make too big records because // getContent() or getContentRaw() returns not content from one record // but from the maximum number of records that can be in RAM memory try { if (copy.isContentRaw()) { addToContentEnsurePreallocating(copy.getContentRaw(), safeMode); while (!copy.willReturnFirstContent()) { addToContentEnsurePreallocating(copy.getContentRaw(), safeMode); } } else { addToContentEnsurePreallocating(copy.getContent(), safeMode); while (!copy.willReturnFirstContent()) { addToContentEnsurePreallocating(copy.getContent(), safeMode); } } } catch (Throwable e) { getBodyPart().setBodyState(BodyPart.BS_EMPTY); size = 0; getBodyPart().getBox().report("+" + e.getMessage() + getBodyPart().getMessageHeader().getSubject(), SOURCE_FILE); //dont notice the user about the error } } protected String conditionallyAppend(String appendTo, String append, boolean shouldAppend) { if (shouldAppend) { return appendTo + append; } else { return appendTo; } } /** * According to the encoding and charset of the bodypart that content this * storage stores, decode given data. * @param data the data to be decoded * @return data decoded according to encoding and charset of the bodypart * that stores this storage. * @throws mujmail.MyException */ private String decodeNotRawBodypartData(String data) throws MyException { if (bodyPart.getHeader().getEncoding() == BodyPart.ENC_BASE64) { return Decode.decodeBase64(data, bodyPart.getHeader().getCharSet()); } else if (bodyPart.getHeader().getEncoding() == BodyPart.ENC_QUOTEDPRINTABLE) { return Decode.decodeQuotedPrintable(data, bodyPart.getHeader().getCharSet()); } else if (bodyPart.getHeader().getEncoding() == BodyPart.ENC_8BIT || bodyPart.getHeader().getCharSet() != BodyPart.CH_NORMAL) { //8bit or not usascii return Decode.decode8bitCharset(data, bodyPart.getHeader().getCharSet()); } else { // 7- bit charset return data; } } /** * <p> * Adds given string to the storage. * Writes data not necessary immediately but only if bigger content for writing * is collected. * Note that it is necessary to call method {@link #flushBuffer()} to store data * persistently before reading it. * </p> * <p> * Stores it according to encoding of the body part which content this storage * stores either as a raw data or as a text data. * </p> * @param bf the string which content store. */ public void addToContentBuffered(String bf) throws Exception { buffer.bufferContent(bf); } /** * Adds given string to the storage. * Writes immediately. * * Stores it according to encoding of the body part which content this storage * stores either as a raw data or as a text data. * @param bf the string which content store. */ public void addToContent(String bf) throws Exception { addToContentBuffered(bf); flushBuffer(); } /** * Stores data written in a buffer persistently. The data can be stored in * buffer when method addToContentBuffered is called. * @throws java.lang.Exception */ public void flushBuffer() throws Exception { buffer.flush(); } /** * Returns true if the content stored in this storage is raw data. * @return true if the content stored in this storage is raw data */ public boolean isContentRaw() { if (getBodyPart().convertedContentMode()) { return true; } else { return (bodyPart.getHeader().getEncoding() == BodyPart.ENC_BASE64 && bodyPart.getHeader().getCharSet() == BodyPart.CH_NORMAL) ? true : false; } } /** * Adds given string to the content of the bodypart. * If passed content cannot be saved tries to shorten the content and * save it partially. * * This method neither sets the size of the bodypart nor the state of the * bodypart. This is done by methods inside class <code>ContentStorage<code/> * that calls this method. * * This is a low-level method, saves bodypart always as a text data regardless * of the body part header. More high level method is addToContent(String). * * Before calling this method, method {@link #ensurePrealocating} is always * called. * * @param content the content of the bodypart * @param safeMode if save mode is true, the content will be not saved * to new persistent storage, but to temporary one * @return the size of content that was added to the content. * content.length if all the content was written. */ protected abstract long addToContent(String content, boolean safeMode) throws Exception; /** * Checks whether the size of bydypart with added content would be less or * equal than given limit. * If the size of bodypart with added content and the bodypart is not * partially savebale. If it is partially saveable, sets its state to * <code>BodyPart.BS_PARTIAL</code>. Than, it throws and exception. * * @param sizeOfAdded the size of added content to bodypart * @throws java.lang.Exception if the size of bodypart with added content * is bigger than the limit. */ /* * TODO: (David) this method is no more needed since the size of bodypart * is now checked while downloading the mail. private void checkSizeOfBodyPart(long sizeOfAdded) throws Exception { if (getSize() + sizeOfAdded > MAX_SIZE_OF_BODYPART) { if (!MessageHeader.canBePartiallySaved(getBodyPart())) { deleteContent(); } else { getBodyPart().setBodyState(BodyPart.BS_PARTIAL); } throw new Exception("The size of this bodypart is larger than given limit."); } } */ /**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -