001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package EDU.purdue.cs.bloat.editor;
022:
023: import java.io.*;
024:
025: import EDU.purdue.cs.bloat.reflect.*;
026:
027: /**
028: * <tt>FieldEditor</tt> provides a means to edit a field of a class. A
029: * <tt>FieldEditor</tt> is created from a <tt>ClassEditor</tt> and a
030: * <tt>reflect.FieldInfo</tt>. A <tt>FieldEditor</tt> knows its name, type
031: * (descriptor), and its constant value (if it has one).
032: *
033: * @see EDU.purdue.cs.bloat.reflect.FieldInfo
034: *
035: * @author Nate Nystrom (<a
036: * href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
037: */
038: public class FieldEditor {
039: private ClassEditor editor;
040:
041: private FieldInfo fieldInfo;
042:
043: private String name;
044:
045: private Type type;
046:
047: private Object constantValue;
048:
049: private boolean isDirty;
050:
051: private boolean isDeleted = false;
052:
053: /**
054: * Creates a new <code>FieldEditor</code> for editing a field in a given
055: * class with the given modifiers, type and name
056: *
057: * @throws IllegalArgumentException
058: * If a field with the desired name already exists in the class
059: */
060: public FieldEditor(final ClassEditor editor, final int modifiers,
061: final Type type, final String name) {
062:
063: this (editor, modifiers, type, name, null);
064: }
065:
066: public FieldEditor(final ClassEditor editor, final int modifiers,
067: final Class type, final String name,
068: final Object constantValue) {
069: this (editor, modifiers, Type.getType(type), name, constantValue);
070: }
071:
072: public FieldEditor(final ClassEditor editor, final int modifiers,
073: final Class type, final String name) {
074: this (editor, modifiers, Type.getType(type), name, null);
075: }
076:
077: /**
078: * Creates a new <code>FieldEditor</code> for editing a field in a given
079: * class with the given modifiers, type, name, and constant value.
080: *
081: * @param modifiers
082: * Fields that have a constant value must be <code>static</code>
083: * and <code>final</code>
084: *
085: * @throws IllegalArgumentException
086: * If a field with the desired name already exists in the class
087: * or if <code>constantValue</code> is non-null and neither a
088: * <code>String</code>, <code>Integer</code>,
089: * <code>Long</code>, <code>Float</code>, nor
090: * <code>Double</code>.
091: */
092: public FieldEditor(final ClassEditor editor, final int modifiers,
093: final Type type, final String name,
094: final Object constantValue) {
095:
096: // Does the class already have a field with this name?
097: final FieldInfo[] fields = editor.fields();
098: for (int i = 0; i < fields.length; i++) {
099: final FieldEditor fe = new FieldEditor(editor, fields[i]);
100: if (fe.name().equals(name)) {
101: final String s = "A field named " + name
102: + " already exists in " + editor.name();
103: throw new IllegalArgumentException(s);
104: }
105: }
106:
107: this .editor = editor;
108:
109: final ConstantPool cp = editor.constants();
110: this .name = name;
111: this .type = type;
112:
113: final int typeIndex = cp.getUTF8Index(this .type.descriptor());
114: final int nameIndex = cp.getUTF8Index(name);
115:
116: final ClassInfo classInfo = editor.classInfo();
117:
118: if (constantValue != null) {
119: // Only static final field may have constant values
120: if (((modifiers & Modifiers.STATIC) == 0)
121: || ((modifiers & Modifiers.FINAL) == 0)) {
122: final String s = "Field "
123: + name
124: + " with a constant value must be static and final";
125: throw new IllegalArgumentException(s);
126: }
127:
128: // Create an entry in the constant pool for the constant value
129: // of this field
130: int valueIndex;
131: if (constantValue instanceof String) {
132: if (!type.equals(Type.STRING)) {
133: final String s = "Can't have field type of "
134: + type.className()
135: + " with a constant value of \""
136: + constantValue + "\"";
137: throw new IllegalArgumentException(s);
138: }
139: valueIndex = cp.getStringIndex((String) constantValue);
140:
141: } else if (constantValue instanceof Integer) {
142: if (!type.equals(Type.INTEGER)) {
143: final String s = "Can't have field type of "
144: + type.className()
145: + " with a constant value of \""
146: + constantValue + "\"";
147: throw new IllegalArgumentException(s);
148: }
149: valueIndex = cp
150: .getIntegerIndex((Integer) constantValue);
151:
152: } else if (constantValue instanceof Long) {
153: if (!type.equals(Type.LONG)) {
154: final String s = "Can't have field type of "
155: + type.className()
156: + " with a constant value of \""
157: + constantValue + "\"";
158: throw new IllegalArgumentException(s);
159: }
160: valueIndex = cp.getLongIndex((Long) constantValue);
161:
162: } else if (constantValue instanceof Float) {
163: if (!type.equals(Type.FLOAT)) {
164: final String s = "Can't have field type of "
165: + type.className()
166: + " with a constant value of \""
167: + constantValue + "\"";
168: throw new IllegalArgumentException(s);
169: }
170: valueIndex = cp.getFloatIndex((Float) constantValue);
171:
172: } else if (constantValue instanceof Double) {
173: if (!type.equals(Type.DOUBLE)) {
174: final String s = "Can't have field type of "
175: + type.className()
176: + " with a constant value of \""
177: + constantValue + "\"";
178: throw new IllegalArgumentException(s);
179: }
180: valueIndex = cp.getDoubleIndex((Double) constantValue);
181:
182: } else {
183: final String s = "Cannot have a constant value of type "
184: + constantValue.getClass().getName();
185: throw new IllegalArgumentException(s);
186: }
187:
188: this .constantValue = constantValue;
189:
190: final int cvNameIndex = cp.getUTF8Index("ConstantValue");
191: this .fieldInfo = classInfo.addNewField(modifiers,
192: typeIndex, nameIndex, cvNameIndex, valueIndex);
193:
194: } else {
195: this .fieldInfo = classInfo.addNewField(modifiers,
196: typeIndex, nameIndex);
197: }
198:
199: this .isDirty = true;
200: }
201:
202: /**
203: * Constructor.
204: *
205: * @param editor
206: * The class containing the field.
207: * @param fieldInfo
208: * The field to edit.
209: *
210: * @see ClassEditor
211: * @see FieldInfo
212: */
213: public FieldEditor(final ClassEditor editor,
214: final FieldInfo fieldInfo) {
215: final ConstantPool cp = editor.constants();
216:
217: this .fieldInfo = fieldInfo;
218: this .editor = editor;
219:
220: int index;
221:
222: index = fieldInfo.nameIndex();
223: name = (String) cp.constantAt(index);
224:
225: index = fieldInfo.typeIndex();
226: final String typeName = (String) cp.constantAt(index);
227: type = Type.getType(typeName);
228:
229: index = fieldInfo.constantValue();
230: constantValue = cp.constantAt(index);
231: this .isDirty = false;
232: }
233:
234: /**
235: * Returns the <tt>ClassEditor</tt> used to edit the class in which this
236: * field resides.
237: */
238: public ClassEditor declaringClass() {
239: return editor;
240: }
241:
242: /**
243: * Returns <tt>true</tt> if this field has been modified.
244: */
245: public boolean isDirty() {
246: return (this .isDirty);
247: }
248:
249: /**
250: * Sets the dirty flag of this method. The dirty flag is <tt>true</tt> if
251: * the method has been modified.
252: *
253: * @throws IllegalStateException This field has been marked for deletion
254: */
255: public void setDirty(final boolean isDirty) {
256: if (this .isDeleted) {
257: final String s = "Cannot change a field once it has been marked "
258: + "for deletion";
259: throw new IllegalStateException(s);
260: }
261:
262: this .isDirty = isDirty;
263: if (isDirty == true) {
264: this .editor.setDirty(true);
265: }
266: }
267:
268: /**
269: * Marks this field for deletion. Once a field has been marked for deletion
270: * all attempts to change it will throw an
271: * <code>IllegalStateException</code>.
272: */
273: public void delete() {
274: this .setDirty(true);
275: this .isDeleted = true;
276: }
277:
278: /**
279: * Returns the raw FieldInfo of the field being edited.
280: */
281: public FieldInfo fieldInfo() {
282: return fieldInfo;
283: }
284:
285: public Object constantValue() {
286: return constantValue;
287: }
288:
289: public boolean isPublic() {
290: return (fieldInfo.modifiers() & Modifiers.PUBLIC) != 0;
291: }
292:
293: public boolean isPrivate() {
294: return (fieldInfo.modifiers() & Modifiers.PRIVATE) != 0;
295: }
296:
297: public boolean isProtected() {
298: return (fieldInfo.modifiers() & Modifiers.PROTECTED) != 0;
299: }
300:
301: /**
302: * Returns true, if the field has package level visibility.
303: */
304: public boolean isPackage() {
305: return (!isPublic() && !isPrivate() && !isProtected());
306: }
307:
308: public boolean isStatic() {
309: return (fieldInfo.modifiers() & Modifiers.STATIC) != 0;
310: }
311:
312: public boolean isFinal() {
313: return (fieldInfo.modifiers() & Modifiers.FINAL) != 0;
314: }
315:
316: public boolean isVolatile() {
317: return (fieldInfo.modifiers() & Modifiers.VOLATILE) != 0;
318: }
319:
320: public boolean isTransient() {
321: return (fieldInfo.modifiers() & Modifiers.TRANSIENT) != 0;
322: }
323:
324: /**
325: * @throws IllegalStateException This field has been marked for deletion
326: */
327: public void setPublic(final boolean flag) {
328: if (this .isDeleted) {
329: final String s = "Cannot change a field once it has been marked "
330: + "for deletion";
331: throw new IllegalStateException(s);
332: }
333:
334: int modifiers = fieldInfo.modifiers();
335:
336: if (flag) {
337: modifiers |= Modifiers.PUBLIC;
338: } else {
339: modifiers &= ~Modifiers.PUBLIC;
340: }
341:
342: fieldInfo.setModifiers(modifiers);
343: this .setDirty(true);
344: }
345:
346: /**
347: * @throws IllegalStateException This field has been marked for deletion
348: */
349: public void setPrivate(final boolean flag) {
350: if (this .isDeleted) {
351: final String s = "Cannot change a field once it has been marked "
352: + "for deletion";
353: throw new IllegalStateException(s);
354: }
355:
356: int modifiers = fieldInfo.modifiers();
357:
358: if (flag) {
359: modifiers |= Modifiers.PRIVATE;
360: } else {
361: modifiers &= ~Modifiers.PRIVATE;
362: }
363:
364: fieldInfo.setModifiers(modifiers);
365: this .setDirty(true);
366: }
367:
368: /**
369: * @throws IllegalStateException This field has been marked for deletion
370: */
371: public void setProtected(final boolean flag) {
372: if (this .isDeleted) {
373: final String s = "Cannot change a field once it has been marked "
374: + "for deletion";
375: throw new IllegalStateException(s);
376: }
377:
378: int modifiers = fieldInfo.modifiers();
379:
380: if (flag) {
381: modifiers |= Modifiers.PROTECTED;
382: } else {
383: modifiers &= ~Modifiers.PROTECTED;
384: }
385:
386: fieldInfo.setModifiers(modifiers);
387: this .setDirty(true);
388: }
389:
390: /**
391: * @throws IllegalStateException This field has been marked for deletion
392: */
393: public void setStatic(final boolean flag) {
394: if (this .isDeleted) {
395: final String s = "Cannot change a field once it has been marked "
396: + "for deletion";
397: throw new IllegalStateException(s);
398: }
399:
400: int modifiers = fieldInfo.modifiers();
401:
402: if (flag) {
403: modifiers |= Modifiers.STATIC;
404: } else {
405: modifiers &= ~Modifiers.STATIC;
406: }
407:
408: fieldInfo.setModifiers(modifiers);
409: this .setDirty(true);
410: }
411:
412: /**
413: * @throws IllegalStateException This field has been marked for deletion
414: */
415: public void setFinal(final boolean flag) {
416: if (this .isDeleted) {
417: final String s = "Cannot change a field once it has been marked "
418: + "for deletion";
419: throw new IllegalStateException(s);
420: }
421:
422: int modifiers = fieldInfo.modifiers();
423:
424: if (flag) {
425: modifiers |= Modifiers.FINAL;
426: } else {
427: modifiers &= ~Modifiers.FINAL;
428: }
429:
430: fieldInfo.setModifiers(modifiers);
431: this .setDirty(true);
432: }
433:
434: /**
435: * @throws IllegalStateException This field has been marked for deletion
436: */
437: public void setTransient(final boolean flag) {
438: if (this .isDeleted) {
439: final String s = "Cannot change a field once it has been marked "
440: + "for deletion";
441: throw new IllegalStateException(s);
442: }
443:
444: int modifiers = fieldInfo.modifiers();
445:
446: if (flag) {
447: modifiers |= Modifiers.TRANSIENT;
448: } else {
449: modifiers &= ~Modifiers.TRANSIENT;
450: }
451:
452: fieldInfo.setModifiers(modifiers);
453: this .setDirty(true);
454: }
455:
456: /**
457: * @throws IllegalStateException This field has been marked for deletion
458: */
459: public void setVolatile(final boolean flag) {
460: if (this .isDeleted) {
461: final String s = "Cannot change a field once it has been marked "
462: + "for deletion";
463: throw new IllegalStateException(s);
464: }
465:
466: int modifiers = fieldInfo.modifiers();
467:
468: if (flag) {
469: modifiers |= Modifiers.VOLATILE;
470: } else {
471: modifiers &= ~Modifiers.VOLATILE;
472: }
473:
474: fieldInfo.setModifiers(modifiers);
475: this .setDirty(true);
476: }
477:
478: /**
479: * Returns the name of the field.
480: */
481: public String name() {
482: return name;
483: }
484:
485: /**
486: * Returns the type of the field.
487: */
488: public Type type() {
489: return type;
490: }
491:
492: /**
493: * Returns a <tt>NameAndType</tt> of the field.
494: */
495: public NameAndType nameAndType() {
496: return (new NameAndType(this .name(), this .type()));
497: }
498:
499: /**
500: * Returns a <code>MemberRef</code> for the field
501: */
502: public MemberRef memberRef() {
503: return (new MemberRef(this .declaringClass().type(), this
504: .nameAndType()));
505: }
506:
507: /**
508: * Commit changes to the field back to the ClassEditor. Note that the field
509: * is committed regardless of whether or not it is dirty.
510: */
511: public void commit() {
512: if (this .isDeleted) {
513: // Even if the field is newly-added, we can still delete it
514: // without problems because its FieldInfo was already noted with
515: // the ClassInfo.
516:
517: final ConstantPool cp = editor.constants();
518: final int nameIndex = cp.getUTF8Index(name);
519: this .editor.classInfo().deleteField(nameIndex);
520:
521: } else {
522: final ConstantPool cp = editor.constants();
523:
524: fieldInfo.setNameIndex(cp.addConstant(Constant.UTF8, name));
525: fieldInfo.setTypeIndex(cp.addConstant(Constant.UTF8, type
526: .descriptor()));
527:
528: if (constantValue != null) {
529: if (constantValue instanceof Long) {
530: fieldInfo.setConstantValue(cp.addConstant(
531: Constant.LONG, constantValue));
532: } else if (constantValue instanceof Float) {
533: fieldInfo.setConstantValue(cp.addConstant(
534: Constant.FLOAT, constantValue));
535: } else if (constantValue instanceof Double) {
536: fieldInfo.setConstantValue(cp.addConstant(
537: Constant.DOUBLE, constantValue));
538: } else if (constantValue instanceof Integer) {
539: fieldInfo.setConstantValue(cp.addConstant(
540: Constant.INTEGER, constantValue));
541: } else if (constantValue instanceof String) {
542: fieldInfo.setConstantValue(cp.addConstant(
543: Constant.STRING, constantValue));
544: }
545: }
546: }
547:
548: // This field is no longer dirty
549: this .isDirty = false;
550: }
551:
552: /**
553: * Print the field.
554: *
555: * @param out
556: * Stream to which to print.
557: */
558: public void print(final PrintStream out) {
559: out.println("field " + name + " " + type);
560: }
561:
562: /**
563: * Returns a String that contains the declaring class name and the name of
564: * the field
565: */
566: public String fullName() {
567: return declaringClass().name() + "." + this .name();
568: }
569:
570: public String toString() {
571: return ("[FieldEditor for " + this .name + this .type + "]");
572: }
573: }
|