001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Evgeniya G. Maenkova
019: * @version $Revision$
020: */package javax.swing.text;
021:
022: import java.text.ParseException;
023: import java.util.BitSet;
024:
025: import javax.swing.JFormattedTextField;
026: import javax.swing.SwingConstants;
027: import javax.swing.text.DocumentFilter.FilterBypass;
028: import javax.swing.text.Position.Bias;
029:
030: import org.apache.harmony.x.swing.internal.nls.Messages;
031:
032: public class MaskFormatter extends DefaultFormatter {
033: private String mask;
034: private String placeholder;
035: private char placeHolderCharacter = ' ';
036: private String validCharacters;
037: private String invalidCharacters;
038: private boolean valueContainsLiteralCharacters = true;
039: private static final String nonLiteralCharacters = "#ULA?*H'";
040: private static final String hexLetters = "abcdef";
041: private BitSet literalMask;
042: private BitSet escapeMask;
043: private static final char ESC = '\'';
044: private NavigationFilter navigationFilter;
045:
046: private class NavigationFilterImpl extends NavigationFilter {
047: public int getNextVisualPositionFrom(final JTextComponent c,
048: final int pos, final Bias bias, final int direction,
049: final Bias[] biasRet) throws BadLocationException {
050: int result = pos;
051: biasRet[0] = bias;
052: do {
053: result = super .getNextVisualPositionFrom(c, result,
054: biasRet[0], direction, biasRet);
055: } while (literalMask.get(result)
056: && !(result == 0 && direction == SwingConstants.EAST)
057: && !(result == c.getDocument().getLength() && direction == SwingConstants.WEST));
058: return result;
059: }
060:
061: public void moveDot(
062: final NavigationFilter.FilterBypass filterBypass,
063: final int dot, final Bias bias) {
064: super .moveDot(filterBypass, dot, bias);
065: }
066:
067: public void setDot(
068: final NavigationFilter.FilterBypass filterBypass,
069: final int dot, final Bias bias) {
070: super .setDot(filterBypass, dot, bias);
071: }
072: }
073:
074: public MaskFormatter(final String mask) throws ParseException {
075: setAllowsInvalid(false);
076: setMask(mask);
077: }
078:
079: public MaskFormatter() {
080: setAllowsInvalid(false);
081: }
082:
083: public String getMask() {
084: return mask;
085: }
086:
087: protected NavigationFilter getNavigationFilter() {
088: if (navigationFilter == null) {
089: navigationFilter = new NavigationFilterImpl();
090: }
091: return navigationFilter;
092: }
093:
094: public String getInvalidCharacters() {
095: return invalidCharacters;
096: }
097:
098: public String getPlaceholder() {
099: return placeholder;
100: }
101:
102: public char getPlaceholderCharacter() {
103: return placeHolderCharacter;
104: }
105:
106: public String getValidCharacters() {
107: return validCharacters;
108: }
109:
110: public boolean getValueContainsLiteralCharacters() {
111: return valueContainsLiteralCharacters;
112: }
113:
114: public void install(final JFormattedTextField ftf) {
115: boolean oldValue = getAllowsInvalid();
116: setAllowsInvalid(true);
117: super .install(ftf);
118: setAllowsInvalid(oldValue);
119: }
120:
121: public void setInvalidCharacters(final String invalidCharacters) {
122: this .invalidCharacters = invalidCharacters;
123: }
124:
125: public void setMask(final String mask) throws ParseException {
126: this .mask = mask;
127: buildMasks();
128: }
129:
130: private void buildMasks() {
131: prepareMasks();
132: int index = -1;
133: int i = 0;
134: while (++index < mask.length()) {
135: boolean result = true;
136: char maskCharacter = mask.charAt(index);
137: boolean isEscapeCharacter = maskCharacter == ESC;
138:
139: if (isEscapeCharacter) {
140: result = true;
141: index++;
142: } else {
143: result = nonLiteralCharacters.indexOf(maskCharacter) < 0;
144: }
145: literalMask.set(i, result);
146: escapeMask.set(i++, result && isEscapeCharacter);
147: }
148: }
149:
150: private void prepareMasks() {
151: literalMask = initMask(literalMask);
152: escapeMask = initMask(escapeMask);
153: }
154:
155: private BitSet initMask(final BitSet set) {
156: BitSet result = set;
157: if (set == null) {
158: result = new BitSet();
159: } else {
160: result.clear();
161: }
162: return result;
163: }
164:
165: public void setPlaceholder(final String placeholder) {
166: this .placeholder = placeholder;
167: }
168:
169: public void setPlaceholderCharacter(final char placeholderCharacter) {
170: this .placeHolderCharacter = placeholderCharacter;
171:
172: }
173:
174: public void setValidCharacters(final String validCharacters) {
175: this .validCharacters = validCharacters;
176: }
177:
178: public void setValueContainsLiteralCharacters(
179: final boolean valueContainsLiteralCharacters) {
180: this .valueContainsLiteralCharacters = valueContainsLiteralCharacters;
181: }
182:
183: public Object stringToValue(final String string)
184: throws ParseException {
185: String parsedString = mask == null ? string
186: : parseString(string);
187: return super .stringToValue(parsedString);
188: }
189:
190: public String valueToString(final Object value)
191: throws ParseException {
192: if ((mask == null) && (escapeMask == null)
193: && (literalMask == null)) {
194: return "";
195: }
196: String result = super .valueToString(value);
197: result = fillAll(result);
198: result = fillByPlaceholder(result);
199: result = fillByPlaceHolderCharacter(result);
200: return result;
201: }
202:
203: private String fillAll(final String string) throws ParseException {
204: String result = "";
205: int length = string.length();
206: int maskLength = mask.length();
207: int escapeLength = escapeMask.cardinality();
208: int literalsLength = literalMask.cardinality();
209:
210: if (maskLength - escapeLength
211: - (valueContainsLiteralCharacters ? 0 : literalsLength) < length) {
212: throw new ParseException(Messages.getString("swing.92"), 0); //$NON-NLS-1$
213: }
214: int index = 0;
215: int maskIndex = 0;
216: int indexInString = 0;
217: char ch;
218: while (indexInString < length) {
219: maskIndex = getNextMaskIndex(index, maskIndex);
220: char maskCharacter = mask.charAt(maskIndex);
221: boolean isLiteral = literalMask.get(index);
222: if (valueContainsLiteralCharacters || !isLiteral) {
223: ch = string.charAt(indexInString);
224: indexInString++;
225: } else {
226: ch = maskCharacter;
227: }
228: result += ch;
229:
230: checkCharacter(maskCharacter, ch, isLiteral, index);
231: index++;
232: maskIndex++;
233: }
234: return result;
235: }
236:
237: private void checkCharacter(final char maskCharacter,
238: final char textCharacter, final boolean isLiteral,
239: final int index) throws ParseException {
240: if (!acceptCharacter(maskCharacter, textCharacter, isLiteral)) {
241: throw new ParseException(
242: Messages.getString("swing.93"), index); //$NON-NLS-1$
243: }
244: }
245:
246: private String fillByPlaceholder(final String string) {
247: int length = string.length();
248: if (length >= mask.length() || placeholder == null
249: || placeholder.length() < length) {
250: return string;
251: } else {
252: return string + getPlaceHolderSubstring(length);
253: }
254: }
255:
256: private String getPlaceHolderSubstring(final int start) {
257: String result = "";
258: int maskIndex = getIndexInMask(start);
259: for (int i = start; i < Math.min(placeholder.length(), mask
260: .length()
261: - escapeMask.cardinality()); i++, maskIndex++) {
262: maskIndex = getNextMaskIndex(i, maskIndex);
263: result += literalMask.get(i) ? mask.charAt(maskIndex)
264: : placeholder.charAt(i);
265: }
266: return result;
267: }
268:
269: private String fillByPlaceHolderCharacter(final String string) {
270: int start = string.length();
271: int maskLength = mask.length();
272: String result = "";
273: int maskIndex = getIndexInMask(start);
274: for (int i = start; i < maskLength - escapeMask.cardinality(); maskIndex++, i++) {
275: maskIndex = getNextMaskIndex(i, maskIndex);
276: result += literalMask.get(i) ? mask.charAt(maskIndex)
277: : placeHolderCharacter;
278: }
279:
280: return string + result;
281: }
282:
283: private int getIndexInMask(final int startIndex) {
284: return escapeMask.get(0, startIndex).cardinality() + startIndex;
285: }
286:
287: private int getNextMaskIndex(final int index,
288: final int prevIndexInMask) {
289: return escapeMask.get(index) ? prevIndexInMask + 1
290: : prevIndexInMask;
291: }
292:
293: private String parseString(final String string)
294: throws ParseException {
295: int length = string.length();
296: int maskLength = mask.length();
297: int index = 0;
298: if (maskLength - length != escapeMask.cardinality()) {
299: throw new ParseException(Messages.getString("swing.94"), 0); //$NON-NLS-1$
300: }
301: int accumulator = 0;
302: String result = "";
303: while (index < length) {
304: char ch = string.charAt(index);
305: accumulator = getNextMaskIndex(index, accumulator);
306: char maskCharacter = mask.charAt(accumulator);
307: boolean isLiteral = literalMask.get(index);
308:
309: if (!isLiteral || valueContainsLiteralCharacters) {
310: result += ch;
311: }
312: checkCharacter(maskCharacter, ch, isLiteral, index);
313: index++;
314: accumulator++;
315: }
316: return result;
317: }
318:
319: private boolean checkString(final int offset, final String string) {
320: int length = string.length();
321: int index = offset;
322: int accumulator = getIndexInMask(index);
323: if (length + offset > literalMask.size()) {
324: return false;
325: }
326: while (index < length + offset) {
327: char ch = string.charAt(index - offset);
328: accumulator = getNextMaskIndex(index, accumulator);
329: if (accumulator >= mask.length()) {
330: return false;
331: }
332: char maskCharacter = mask.charAt(accumulator);
333: boolean isLiteral = literalMask.get(index);
334: try {
335: checkCharacter(maskCharacter, ch, isLiteral, index);
336: } catch (ParseException e) {
337: return false;
338: }
339: index++;
340: accumulator++;
341: }
342: return true;
343: }
344:
345: private boolean isValid(final char ch) {
346: boolean isValidCharacter = validCharacters == null
347: || validCharacters.indexOf(ch) >= 0;
348: boolean isNotInvalid = invalidCharacters == null
349: || invalidCharacters.indexOf(ch) < 0;
350: return isValidCharacter && isNotInvalid;
351: }
352:
353: private boolean acceptCharacter(final char maskCharacter,
354: final char ch, final boolean compareLiterals) {
355: if (compareLiterals) {
356: return acceptCharacter(maskCharacter, ch);
357: }
358: switch (maskCharacter) {
359: case '#':
360: return Character.isDigit(ch) && isValid(ch);
361: case 'U':
362: return Character.isLetter(ch) && isValid(ch);
363: case 'L':
364: return Character.isLetter(ch) && isValid(ch);
365: case 'A':
366: return (Character.isLetter(ch) || Character.isDigit(ch))
367: && isValid(ch);
368: case '?':
369: return Character.isLetter(ch) && isValid(ch);
370: case '*':
371: return isValid(ch);
372: case 'H':
373: char chToL = Character.toLowerCase(ch);
374: return Character.isDigit(ch)
375: || hexLetters.indexOf(chToL) >= 0 && isValid(ch);
376: default:
377: return acceptCharacter(maskCharacter, ch);
378: }
379: }
380:
381: private boolean acceptCharacter(final char maskCharacter,
382: final char ch) {
383: return maskCharacter == ch && isValid(ch);
384: }
385:
386: final void replaceImpl(final FilterBypass filterBypass,
387: final int offset, final int length, final String text,
388: final AttributeSet attrs) throws BadLocationException {
389: if (getAllowsInvalid()) {
390: filterBypass.replace(offset, length, text, attrs);
391: } else if (checkString(offset, text)) {
392: String text1 = addLiteralSympolsToString(offset, text);
393: filterBypass.replace(offset, text1.length(), text1, null);
394: }
395: }
396:
397: //This method will not be called to get valid text (as text length will be
398: //increased). So if getAllows invalid return false it does nothing
399: final void insertStringImpl(final FilterBypass filterBypass,
400: final int offset, final String string,
401: final AttributeSet attrs) throws BadLocationException {
402: if (getAllowsInvalid()) {
403: filterBypass.insertString(offset, string, attrs);
404: }
405: }
406:
407: void removeImpl(final FilterBypass filterBypass, final int offset,
408: final int length) throws BadLocationException {
409: if (getAllowsInvalid()) {
410: filterBypass.remove(offset, length);
411: } else {
412: String text = getSubstring(offset, getMaxLengthToRemove(
413: filterBypass, offset, length));
414: filterBypass.replace(offset, text.length(), text, null);
415: JFormattedTextField textField = getFormattedTextField();
416: //perhaps it is temporary solution
417: textField.setCaretPosition(offset);
418: }
419: }
420:
421: private String getSubstring(final int offset, final int length) {
422: int start = offset;
423: String result = "";
424: int maskIndex = getIndexInMask(start);
425: int literalsNumber = literalMask.get(offset, offset + length)
426: .cardinality();
427: int escapeNumber = escapeMask.get(offset, offset + length)
428: .cardinality();
429: for (int i = start; i < start + length + literalsNumber
430: + escapeNumber; maskIndex++, i++) {
431: maskIndex = getNextMaskIndex(i, maskIndex);
432: result += literalMask.get(i) ? mask.charAt(maskIndex)
433: : placeHolderCharacter;
434: }
435: return result;
436: }
437:
438: private String addLiteralSympolsToString(final int offset,
439: final String text) {
440: String result = text;
441: int index = offset + text.length();
442: int maskIndex = getIndexInMask(index);
443: while (literalMask.get(index) && maskIndex < mask.length()) {
444: maskIndex = getNextMaskIndex(index, maskIndex);
445: result += mask.charAt(maskIndex);
446: index++;
447: maskIndex++;
448: }
449: return result;
450: }
451: }
|