1 /* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.streamhtmlparser.util; 18 19 /** 20 * Records (stores) characters supplied one at a time conditional on 21 * whether recording is currently enabled. 22 * 23 * <p>When {@link #maybeRecord(char)} is called, it will add the 24 * supplied character to the recording buffer but only if 25 * recording is in progress. This is useful in our 26 * {@link com.google.security.streamhtmlparser.HtmlParser} 27 * as the caller logic to enable/disable recording is decoupled from the logic 28 * of recording. 29 * 30 * <p>This is a specialized class - of no use to external code - 31 * which aims to be 100% compatible with the corresponding logic 32 * in the C-version of the HtmlParser, specifically in 33 * <code>statemachine.c</code>. In particular: 34 * <ul> 35 * <li>The {@code startRecording()} and {@code stopRecording()} methods 36 * may be called repeatedly without interleaving since the C version is 37 * not guaranteed to interleave them. 38 * <li>There is a size limit to the recording buffer as set in 39 * {@link #RECORDING_BUFFER_SIZE}. Once the size is 40 * reached, no further characters are recorded regardless of whether 41 * recording is currently enabled. 42 * </ul> 43 */ 44 public class CharacterRecorder { 45 46 /** 47 * How many characters can be recorded before stopping to accept new 48 * ones. Set to one less than in the C-version as we do not need 49 * to reserve a character for the terminating null. 50 */ 51 public static final int RECORDING_BUFFER_SIZE = 255; 52 53 /** 54 * This is where characters provided for recording are stored. Given 55 * that the <code>CharacterRecorder</code> object is re-used, might as well 56 * allocate the full size from the get-go. 57 */ 58 private final StringBuilder sb; 59 60 /** Holds whether we are currently recording characters or not. */ 61 private boolean recording; 62 63 /** 64 * Constructs an empty character recorder of fixed size currently 65 * not recording. See {@link #RECORDING_BUFFER_SIZE} for the size. 66 */ CharacterRecorder()67 public CharacterRecorder() { 68 sb = new StringBuilder(RECORDING_BUFFER_SIZE); 69 recording = false; 70 } 71 72 /** 73 * Constructs a character recorder of fixed size that is a copy 74 * of the one provided. In particular it has the same recording 75 * setting and the same contents. 76 * 77 * @param aCharacterRecorder the {@code CharacterRecorder} to copy 78 */ CharacterRecorder(CharacterRecorder aCharacterRecorder)79 public CharacterRecorder(CharacterRecorder aCharacterRecorder) { 80 recording = aCharacterRecorder.recording; 81 sb = new StringBuilder(RECORDING_BUFFER_SIZE); 82 sb.append(aCharacterRecorder.getContent()); 83 } 84 85 /** 86 * Enables recording for incoming characters. The recording buffer is cleared 87 * of content it may have contained. 88 */ startRecording()89 public void startRecording() { 90 // This is very fast, no memory (re-) allocation will take place. 91 sb.setLength(0); 92 recording = true; 93 } 94 95 /** 96 * Disables recording further characters. 97 */ stopRecording()98 public void stopRecording() { 99 recording = false; 100 } 101 102 /** 103 * Records the {@code input} if recording is currently on and we 104 * have space available in the buffer. If recording is not 105 * currently on, this method will not perform any action. 106 * 107 * @param input the character to record 108 */ maybeRecord(char input)109 public void maybeRecord(char input) { 110 if (recording && (sb.length() < RECORDING_BUFFER_SIZE)) { 111 sb.append(input); 112 } 113 } 114 115 /** 116 * Empties the underlying storage but does not change the recording 117 * state [i.e whether we are recording or not incoming characters]. 118 */ clear()119 public void clear() { 120 sb.setLength(0); 121 } 122 123 /** 124 * Empties the underlying storage and resets the recording indicator 125 * to indicate we are not recording currently. 126 */ reset()127 public void reset() { 128 clear(); 129 recording = false; 130 } 131 132 /** 133 * Returns the characters recorded in a {@code String} form. This 134 * method has no side-effects, the characters remain stored as is. 135 * 136 * @return the contents in a {@code String} form 137 */ getContent()138 public String getContent() { 139 return sb.toString(); 140 } 141 142 /** 143 * Returns whether or not we are currently recording incoming characters. 144 * 145 * @return {@code true} if we are recording, {@code false} otherwise 146 */ isRecording()147 public boolean isRecording() { 148 return recording; 149 } 150 151 /** 152 * Returns the full state of the object in a human readable form. The 153 * format of the returned {@code String} is not specified and is 154 * subject to change. 155 * 156 * @return the full state of this object 157 */ 158 @Override toString()159 public String toString() { 160 return String.format("In recording: %s; Value: %s", isRecording(), 161 sb.toString()); 162 } 163 } 164