Initial support for "oneof" keyword for internal testing
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
index 41f1d91..57722aa 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
@@ -50,7 +50,7 @@
   '}' (';')?;
 
 MessageElement:
-  Option | Extensions | ComplexType | MessageField | TypeExtension;
+  Option | Extensions | ComplexType | MessageField | TypeExtension | OneOf;
 
 Range:
   from=LONG ('to' to=RangeMax)?;
@@ -66,6 +66,14 @@
 
 GroupElement:
   Option | IndexedElement | ComplexType | TypeExtension | Extensions;
+  
+OneOf:
+  'oneof' name=Name '{'
+  elements+=OneOfElement*
+  '}' (';')?;
+  
+OneOfElement returns MessageElement:
+  Extensions | UnmodifiedMessageField;
 
 Extensions:
   'extensions' ranges+=Range (',' ranges+=Range)* (';')+;
@@ -74,6 +82,10 @@
   =>modifier=Modifier type=TypeLink name=Name '=' index=(LONG | HEX)
   ('[' (fieldOptions+=FieldOption (',' fieldOptions+=FieldOption)*)? ']')? (';')+;
 
+UnmodifiedMessageField returns MessageField:
+  type=TypeLink name=Name '=' index=(LONG | HEX)
+  ('[' (fieldOptions+=FieldOption (',' fieldOptions+=FieldOption)*)? ']')? (';')+;
+
 enum Modifier:
   required | optional | repeated;
 
@@ -219,7 +231,7 @@
   ID | SafeReservedWord;
   
 ReservedWord:  
-  SafeReservedWord | 'group';
+  SafeReservedWord | 'group' | 'oneof';
 
 SafeReservedWord:
   'package' | 'import' | 'public' | 'option' | 'extend' | 'message' | 'optional' | 'required' | 'repeated' |
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
index 68b658a..26c8496 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
@@ -12,6 +12,7 @@
 import org.eclipse.xtext.conversion.IValueConverterService;
 import org.eclipse.xtext.naming.IQualifiedNameConverter;
 import org.eclipse.xtext.naming.IQualifiedNameProvider;
+import org.eclipse.xtext.parser.IAstFactory;
 import org.eclipse.xtext.parser.antlr.ISyntaxErrorMessageProvider;
 import org.eclipse.xtext.resource.IGlobalServiceProvider;
 import org.eclipse.xtext.resource.XtextResource;
@@ -23,6 +24,7 @@
 import com.google.eclipse.protobuf.linking.ProtobufResource;
 import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameConverter;
 import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameProvider;
+import com.google.eclipse.protobuf.parser.ProtobufAstFactory;
 import com.google.eclipse.protobuf.resource.FastXtextResourceSet;
 import com.google.eclipse.protobuf.resource.GlobalResourceServiceProvider;
 import com.google.eclipse.protobuf.scoping.ExtensionRegistryProvider;
@@ -46,6 +48,10 @@
   public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
     return ProtobufQualifiedNameConverter.class;
   }
+  
+  @Override public Class<? extends IAstFactory> bindIAstFactory() {
+    return ProtobufAstFactory.class;
+  }
 
   @Override public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
     return ProtobufQualifiedNameProvider.class;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/IndexedElements.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/IndexedElements.java
index d5554a3..6a8208f 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/IndexedElements.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/IndexedElements.java
@@ -21,6 +21,8 @@
 
 import com.google.eclipse.protobuf.protobuf.FieldOption;
 import com.google.eclipse.protobuf.protobuf.IndexedElement;
+import com.google.eclipse.protobuf.protobuf.MessageElement;
+import com.google.eclipse.protobuf.protobuf.OneOf;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -51,16 +53,24 @@
    * @return the calculated value for the index of the given element.
    */
   public long calculateNewIndexFor(IndexedElement e) {
-    long index = 0;
     EObject type = e.eContainer();
-    for (EObject o : type.eContents()) {
-      if (o == e || !(o instanceof IndexedElement)) {
-        continue;
-      }
-      index = max(index, indexOf((IndexedElement) o));
-    }
+    long index = findMaxIndex(type.eContents());
     return ++index;
   }
+  
+  private long findMaxIndex(Iterable<? extends EObject> elements) {
+    long maxIndex = 0;
+
+    for (EObject e : elements) {
+      if (e instanceof OneOf) {
+        maxIndex = max(maxIndex, findMaxIndex(((OneOf) e).getElements()));
+      } else if (e instanceof IndexedElement) {
+        maxIndex  = max(maxIndex, indexOf((IndexedElement) e));
+      }
+    }
+    
+    return maxIndex;
+  }
 
   /**
    * Returns the name of the given <code>{@link IndexedElement}</code>.
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java
index 0611784..cb48985 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java
@@ -22,11 +22,14 @@
 import com.google.eclipse.protobuf.protobuf.Message;
 import com.google.eclipse.protobuf.protobuf.MessageElement;
 import com.google.eclipse.protobuf.protobuf.MessageField;
+import com.google.eclipse.protobuf.protobuf.OneOf;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.eclipse.protobuf.protobuf.TypeExtension;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
+import org.eclipse.emf.ecore.EObject;
+
 /**
  * Utility methods related to <code>{@link Message}</code>s.
  *
@@ -64,11 +67,23 @@
    */
   public Collection<MessageField> fieldsOf(Message message) {
     List<MessageField> fields = newArrayList();
-    for (MessageElement e : message.getElements()) {
-      if (e instanceof MessageField) {
-        fields.add((MessageField) e);
+    fieldsOf(message, fields);
+    return unmodifiableList(fields);
+  }
+
+  private void fieldsOf(EObject message, List<MessageField> fields) {
+    if (message instanceof Message) {
+      for (MessageElement e : ((Message) message).getElements()) {
+        if (e instanceof MessageField) {
+          fields.add((MessageField) e);
+        }
+      }
+    } else if (message instanceof OneOf) {
+      for (MessageElement e : ((OneOf) message).getElements()) {
+        if (e instanceof MessageField) {
+          fields.add((MessageField) e);
+        }
       }
     }
-    return unmodifiableList(fields);
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/parser/ProtobufAstFactory.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/parser/ProtobufAstFactory.java
new file mode 100644
index 0000000..dc8dc4f
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/parser/ProtobufAstFactory.java
@@ -0,0 +1,34 @@
+package com.google.eclipse.protobuf.parser;
+import com.google.eclipse.protobuf.protobuf.MessageField;
+import com.google.eclipse.protobuf.protobuf.Modifier;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.conversion.ValueConverterException;
+import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.parser.DefaultEcoreElementFactory;
+
+public class ProtobufAstFactory extends DefaultEcoreElementFactory {
+  private static final String UNMODIFIEDMESSAGEFIELD_RULE = "UnmodifiedMessageField";
+  private static final String ONEOFELEMENT_RULE = "OneOfElement";
+
+  @Override
+  public void add(EObject object, String feature, Object value, String ruleName, INode node)
+      throws ValueConverterException {
+    // TODO(foremans): Auto-generated method stub
+    super.add(object, feature, value, ruleName, node);
+    if (ONEOFELEMENT_RULE.equals(ruleName) && value instanceof MessageField) {
+      MessageField field = (MessageField) value;
+      field.setModifier(Modifier.OPTIONAL);
+    }
+  }
+  
+  @Override
+  public void set(EObject object, String feature, Object value, String ruleName, INode node)
+      throws ValueConverterException {
+    super.set(object, feature, value, ruleName, node);
+    if (UNMODIFIEDMESSAGEFIELD_RULE.equals(ruleName)) {
+      MessageField field = (MessageField) object;
+      field.setModifier(Modifier.OPTIONAL);
+    }
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
index 12cb9e1..82a7ed5 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
@@ -33,6 +33,7 @@
 import com.google.eclipse.protobuf.protobuf.IndexedElement;
 import com.google.eclipse.protobuf.protobuf.Message;
 import com.google.eclipse.protobuf.protobuf.MessageElement;
+import com.google.eclipse.protobuf.protobuf.OneOf;
 import com.google.eclipse.protobuf.protobuf.Package;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.eclipse.protobuf.protobuf.ProtobufElement;
@@ -72,27 +73,39 @@
     if (isNameNull(e)) {
       return; // we already show an error if name is null, no need to go further.
     }
-    long index = indexedElements.indexOf(e);
+    
     EObject container = e.eContainer();
     if (container instanceof Message) {
       Message message = (Message) container;
-      for (MessageElement element : message.getElements()) {
-        if (!(element instanceof IndexedElement)) {
-          continue;
+      Iterable<MessageElement> elements = message.getElements();
+      checkTagNumerIsUnique(e, message, elements);
+    }
+  }
+
+  private boolean checkTagNumerIsUnique(IndexedElement e, EObject message, 
+      Iterable<MessageElement> elements) {
+    long index = indexedElements.indexOf(e);
+
+    for (MessageElement element : elements) {
+      if (element instanceof OneOf) {
+        if (!checkTagNumerIsUnique(e, message, ((OneOf) element).getElements())) {
+          return false;
         }
+      } else if (element instanceof IndexedElement) {
         IndexedElement other = (IndexedElement) element;
         if (other == e) {
-          break;
+          return true;
         }
-        if (indexedElements.indexOf(other) != index) {
-          continue;
+        if (indexedElements.indexOf(other) == index) {
+          QualifiedName messageName = qualifiedNameProvider.getFullyQualifiedName(message);
+          String msg = format(fieldNumberAlreadyUsed, index, messageName.toString(), nameResolver.nameOf(other));
+          invalidTagNumberError(msg, e);
+          return false;
         }
-        QualifiedName messageName = qualifiedNameProvider.getFullyQualifiedName(message);
-        String msg = format(fieldNumberAlreadyUsed, index, messageName.toString(), nameResolver.nameOf(other));
-        invalidTagNumberError(msg, e);
-        break;
       }
     }
+    
+    return true;
   }
 
   @Check public void checkTagNumberIsGreaterThanZero(IndexedElement e) {